Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / utility / cloud_print / pwg_encoder.cc
index c4aeded..03eac5c 100644 (file)
@@ -16,16 +16,21 @@ namespace cloud_print {
 namespace {
 
 const uint32 kBitsPerColor = 8;
-const uint32 kColorSpace = 19;  // sRGB.
 const uint32 kColorOrder = 0;  // chunky.
-const uint32 kNumColors = 3;
-const uint32 kBitsPerPixel = kNumColors * kBitsPerColor;
 
-const char kPwgKeyword[] = "RaS2";
+// Coefficients used to convert from RGB to monochrome.
+const uint32 kRedCoefficient = 2125;
+const uint32 kGreenCoefficient = 7154;
+const uint32 kBlueCoefficient = 0721;
+const uint32 kColorCoefficientDenominator = 10000;
+
+const char* kPwgKeyword = "RaS2";
 
 const uint32 kHeaderSize = 1796;
-const uint32 kHeaderHwResolutionHorizontal = 276;
-const uint32 kHeaderHwResolutionVertical = 280;
+const uint32 kHeaderCupsDuplex = 272;
+const uint32 kHeaderCupsHwResolutionHorizontal = 276;
+const uint32 kHeaderCupsHwResolutionVertical = 280;
+const uint32 kHeaderCupsTumble = 368;
 const uint32 kHeaderCupsWidth = 372;
 const uint32 kHeaderCupsHeight = 376;
 const uint32 kHeaderCupsBitsPerColor = 384;
@@ -35,174 +40,220 @@ const uint32 kHeaderCupsColorOrder = 396;
 const uint32 kHeaderCupsColorSpace = 400;
 const uint32 kHeaderCupsNumColors = 420;
 const uint32 kHeaderPwgTotalPageCount = 452;
+const uint32 kHeaderPwgCrossFeedTransform = 456;
+const uint32 kHeaderPwgFeedTransform = 460;
 
 const int kPwgMaxPackedRows = 256;
 
 const int kPwgMaxPackedPixels = 128;
 
-inline int FlipIfNeeded(bool flip, int current, int total) {
-  return flip ? total - current : current;
-}
-
-}  // namespace
+struct RGBA8 {
+  uint8 red;
+  uint8 green;
+  uint8 blue;
+  uint8 alpha;
+};
 
-PwgEncoder::PwgEncoder() {}
+struct BGRA8 {
+  uint8 blue;
+  uint8 green;
+  uint8 red;
+  uint8 alpha;
+};
 
-inline void encodePixelFromRGBA(const uint8* pixel, std::string* output) {
-  output->push_back(static_cast<char>(pixel[0]));
-  output->push_back(static_cast<char>(pixel[1]));
-  output->push_back(static_cast<char>(pixel[2]));
+template <class InputStruct>
+inline 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));
 }
 
-inline void encodePixelFromBGRA(const uint8* pixel, std::string* output) {
-  output->push_back(static_cast<char>(pixel[2]));
-  output->push_back(static_cast<char>(pixel[1]));
-  output->push_back(static_cast<char>(pixel[0]));
+template <class InputStruct>
+inline 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));
 }
 
+}  // namespace
+
+PwgEncoder::PwgEncoder() {}
+
 void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
   output->clear();
   output->append(kPwgKeyword, 4);
 }
 
-void PwgEncoder::EncodePageHeader(const BitmapImage& image, const uint32 dpi,
-                                  const uint32 total_pages,
+void PwgEncoder::EncodePageHeader(const BitmapImage& image,
+                                  const PwgHeaderInfo& pwg_header_info,
                                   std::string* output) const {
   char header[kHeaderSize];
   memset(header, 0, kHeaderSize);
-  base::WriteBigEndian<uint32>(header + kHeaderHwResolutionHorizontal, dpi);
-  base::WriteBigEndian<uint32>(header + kHeaderHwResolutionVertical, dpi);
+
+  uint32 num_colors =
+      pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3;
+  uint32 bits_per_pixel = num_colors * kBitsPerColor;
+
+  base::WriteBigEndian<uint32>(header + kHeaderCupsDuplex,
+                               pwg_header_info.duplex ? 1 : 0);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionHorizontal,
+                               pwg_header_info.dpi);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionVertical,
+                               pwg_header_info.dpi);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsTumble,
+                               pwg_header_info.tumble ? 1 : 0);
   base::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
   base::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
                                image.size().height());
   base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
-  base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel, kBitsPerPixel);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel,
+                               bits_per_pixel);
   base::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
-                               (kBitsPerPixel * image.size().width() + 7) / 8);
+                               (bits_per_pixel * image.size().width() + 7) / 8);
   base::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
-  base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace, kColorSpace);
-  base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, kNumColors);
-  base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount, total_pages);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace,
+                               pwg_header_info.color_space);
+  base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, num_colors);
+  base::WriteBigEndian<uint32>(header + kHeaderPwgCrossFeedTransform,
+                               pwg_header_info.flipx ? -1 : 1);
+  base::WriteBigEndian<uint32>(header + kHeaderPwgFeedTransform,
+                               pwg_header_info.flipy ? -1 : 1);
+  base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount,
+                               pwg_header_info.total_pages);
   output->append(header, kHeaderSize);
 }
 
-bool PwgEncoder::EncodeRowFrom32Bit(const uint8* row, const int width,
-                                    const int color_space,
-                                    std::string* output) const {
-  void (*pixel_encoder)(const uint8*, std::string*);
-  switch (color_space) {
-    case BitmapImage::RGBA:
-      pixel_encoder = &encodePixelFromRGBA;
-      break;
-    case BitmapImage::BGRA:
-      pixel_encoder = &encodePixelFromBGRA;
-      break;
-    default:
-      LOG(ERROR) << "Unsupported colorspace.";
-      return false;
-  }
-
-  // Converts the list of uint8 to uint32 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 template function P on the original
-  // array to avoid endian problems.
-  const uint32* pos = reinterpret_cast<const uint32*>(row);
-  const uint32* row_end = pos + width;
+template <typename InputStruct, class RandomAccessIterator>
+void PwgEncoder::EncodeRow(RandomAccessIterator pos,
+                           RandomAccessIterator row_end,
+                           bool monochrome,
+                           std::string* output) const {
   // 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 128) can be encoded
+  // 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.
 
-  // It is usually better to encode every largest sequence of > 2 identical
-  // pixels together because it saves the most space. Every other pixel should
-  // be encoded in the smallest number of generic sequences.
+  // 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) {
-    const uint32* it = pos + 1;
-    const uint32* end = std::min(pos + kPwgMaxPackedPixels, 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++;
+      ++it;
     }
     if (it != pos + 1) {  // More than one pixel
       output->push_back(static_cast<char>((it - pos) - 1));
-      pixel_encoder(reinterpret_cast<const uint8*>(pos), output);
+      if (monochrome)
+        encodePixelToMonochrome<InputStruct>(&*pos, output);
+      else
+        encodePixelToRGB<InputStruct>(&*pos, output);
       pos = it;
     } else {
-      // Finds how many pixels each different from the previous one (up to 128).
+      // 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++;
+        ++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--;
+        --it;
       }
       output->push_back(static_cast<char>(1 - (it - pos)));
       while (pos != it) {
-        pixel_encoder(reinterpret_cast<const uint8*>(pos++), output);
+        if (monochrome)
+          encodePixelToMonochrome<InputStruct>(&*pos, output);
+        else
+          encodePixelToRGB<InputStruct>(&*pos, output);
+        ++pos;
       }
     }
   }
-  return true;
 }
 
 inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
-                                       int row) const {
-  return image.pixel_data() + row * image.size().width() * image.channels();
+                                       int row,
+                                       bool flipy) const {
+  return image.GetPixel(
+      gfx::Point(0, flipy ? image.size().height() - 1 - row : row));
 }
 
 // Given a pointer to a struct Image, create a PWG of the image and
-// put the compressed image data in the std::string.  Returns true on success.
-// The content of the std::string is undefined on failure.
+// put the compressed image data in the string.  Returns true on success.
+// The content of the string is undefined on failure.
 bool PwgEncoder::EncodePage(const BitmapImage& image,
-                            const uint32 dpi,
-                            const uint32 total_pages,
-                            std::string* output,
-                            bool rotate) const {
-  // For now only some 4-channel colorspaces are supported.
-  if (image.channels() != 4) {
-    LOG(ERROR) << "Unsupported colorspace.";
-    return false;
+                            const PwgHeaderInfo& pwg_header_info,
+                            std::string* output) const {
+  // pwg_header_info.color_space can only contain color spaces that are
+  // supported, so no sanity check is needed.
+  switch (image.colorspace()) {
+    case BitmapImage::RGBA:
+      return EncodePageWithColorspace<RGBA8>(image, pwg_header_info, output);
+
+    case BitmapImage::BGRA:
+      return EncodePageWithColorspace<BGRA8>(image, pwg_header_info, output);
+
+    default:
+      LOG(ERROR) << "Unsupported colorspace.";
+      return false;
   }
+}
 
-  EncodePageHeader(image, dpi, total_pages, output);
+template <typename InputStruct>
+bool PwgEncoder::EncodePageWithColorspace(const BitmapImage& image,
+                                          const PwgHeaderInfo& pwg_header_info,
+                                          std::string* output) const {
+  bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY;
+  EncodePageHeader(image, pwg_header_info, output);
 
+  // Ensure no integer overflow.
+  CHECK(image.size().width() < INT_MAX / image.channels());
   int row_size = image.size().width() * image.channels();
-  scoped_ptr<uint8[]> current_row_cpy(new uint8[row_size]);
 
   int row_number = 0;
-  int total_rows = image.size().height();
-  while (row_number < total_rows) {
+  while (row_number < image.size().height()) {
     const uint8* current_row =
-        GetRow(image, FlipIfNeeded(rotate, row_number++, total_rows));
+        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, FlipIfNeeded(rotate, row_number, total_rows)),
+                   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));
 
-    if (rotate) {
-      memcpy(current_row_cpy.get(), current_row, row_size);
-      std::reverse(reinterpret_cast<uint32*>(current_row_cpy.get()),
-                   reinterpret_cast<uint32*>(current_row_cpy.get() + row_size));
-      current_row = current_row_cpy.get();
-    }
-
     // Both supported colorspaces have a 32-bit pixels information.
-    if (!EncodeRowFrom32Bit(
-            current_row, image.size().width(), image.colorspace(), output)) {
-      return false;
+    // Converts the list of uint8 to uint32 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* pos = reinterpret_cast<const uint32*>(current_row);
+    const uint32* 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*>(row_end),
+                             std::reverse_iterator<const uint32*>(pos),
+                             monochrome,
+                             output);
     }
   }
   return true;