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