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