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.
5 #include "chrome/utility/cloud_print/pwg_encoder.h"
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"
14 namespace cloud_print
{
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
;
24 const char kPwgKeyword
[] = "RaS2";
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;
39 const int kPwgMaxPackedRows
= 256;
41 const int kPwgMaxPackedPixels
= 128;
45 PwgEncoder::PwgEncoder() {}
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]));
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]));
59 void PwgEncoder::EncodeDocumentHeader(std::string
* output
) const {
61 output
->append(kPwgKeyword
, 4);
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
);
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
;
93 case BitmapImage::BGRA
:
94 pixel_encoder
= &encodePixelFromBGRA
;
97 LOG(ERROR
) << "Unsupported colorspace.";
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.
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
);
121 // Counts how many identical pixels (up to 128).
122 while (it
!= end
&& *pos
== *it
) {
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
);
130 // Finds how many pixels each different from the previous one (up to 128).
131 while (it
!= end
&& *it
!= *(it
- 1)) {
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)) {
141 output
->push_back(static_cast<char>(1 - (it
- pos
)));
143 pixel_encoder(reinterpret_cast<const uint8
*>(pos
++), output
);
150 inline const uint8
* PwgEncoder::GetRow(const BitmapImage
& image
,
152 return image
.pixel_data() + row
* image
.size().width() * image
.channels();
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
,
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.";
168 EncodePageHeader(image
, dpi
, total_pages
, output
);
170 int row_size
= image
.size().width() * image
.channels();
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
++;
183 output
->push_back(static_cast<char>(num_identical_rows
- 1));
185 // Both supported colorspaces have a 32-bit pixels information.
186 if (!EncodeRowFrom32Bit(
187 current_row
, image
.size().width(), image
.colorspace(), output
)) {
194 } // namespace cloud_print