pwg_encoder.cc 9.8 KB

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