1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // header and chunks writing
16 //
17 // Author: Skal (pascal.massimino@gmail.com)
18
19 #include <math.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23
24 #include "sjpegi.h"
25
26 namespace sjpeg {
27
28 ////////////////////////////////////////////////////////////////////////////////
29 // Headers
30 //
31 // NOTE(skal): all chunks start with a startcode '0xff??' (0xffd8 e.g),
32 // followed by the size of the payload *not counting the startcode*!
33 // That's why you often find these 'Reserve(data_size + 2)' below, the '+2'
34 // accounting for the 0xff?? startcode size.
35
36 static const uint8_t kHeaderAPP0[] = {
37 0xff, 0xd8, // SOI
38 0xff, 0xe0, 0x00, 0x10, // APP0
39 0x4a, 0x46, 0x49, 0x46, 0x00, // 'JFIF'
40 0x01, 0x01, // v1.01
41 0x00, 0x00, 0x01, 0x00, 0x01, // aspect ratio = 1:1
42 0x00, 0x00 // thumbnail width/height
43 };
44
WriteAPP0()45 void Encoder::WriteAPP0() { // SOI + APP0
46 ok_ = ok_ && bw_.Reserve(sizeof(kHeaderAPP0));
47 if (!ok_) return;
48 bw_.PutBytes(kHeaderAPP0, sizeof(kHeaderAPP0));
49 }
50
WriteAPPMarkers(const std::string & data)51 bool Encoder::WriteAPPMarkers(const std::string& data) {
52 if (data.size() == 0) return true;
53 const size_t data_size = data.size();
54 ok_ = ok_ && bw_.Reserve(data_size);
55 if (!ok_) return false;
56 bw_.PutBytes(reinterpret_cast<const uint8_t*>(data.data()), data.size());
57 return true;
58 }
59
WriteEXIF(const std::string & data)60 bool Encoder::WriteEXIF(const std::string& data) {
61 if (data.size() == 0) return true;
62 const uint8_t kEXIF[] = "Exif\0";
63 const size_t kEXIF_len = 6; // includes the \0's
64 const size_t data_size = data.size() + kEXIF_len + 2;
65 if (data_size > 0xffff) return false;
66 ok_ = ok_ && bw_.Reserve(data_size + 2);
67 if (!ok_) return false;
68 bw_.PutByte(0xff);
69 bw_.PutByte(0xe1);
70 bw_.PutByte((data_size >> 8) & 0xff);
71 bw_.PutByte((data_size >> 0) & 0xff);
72 bw_.PutBytes(kEXIF, kEXIF_len);
73 bw_.PutBytes(reinterpret_cast<const uint8_t*>(data.data()), data.size());
74 return true;
75 }
76
WriteICCP(const std::string & data)77 bool Encoder::WriteICCP(const std::string& data) {
78 if (data.size() == 0) return true;
79 size_t data_size = data.size();
80 const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data.data());
81 const uint8_t kICCP[] = "ICC_PROFILE";
82 const size_t kICCP_len = 12; // includes the \0
83 const size_t chunk_size_max = 0xffff - kICCP_len - 4;
84 size_t max_chunk = (data_size + chunk_size_max - 1) / chunk_size_max;
85 if (max_chunk >= 256) return false;
86 size_t seq = 1;
87 while (data_size > 0) {
88 size_t size = data_size;
89 if (size > chunk_size_max) size = chunk_size_max;
90 ok_ = ok_ && bw_.Reserve(size + kICCP_len + 4 + 2);
91 if (!ok_) return false;
92 bw_.PutByte(0xff);
93 bw_.PutByte(0xe2);
94 bw_.PutByte(((size + kICCP_len + 4) >> 8) & 0xff);
95 bw_.PutByte(((size + kICCP_len + 4) >> 0) & 0xff);
96 bw_.PutBytes(kICCP, kICCP_len);
97 bw_.PutByte(seq & 0xff);
98 bw_.PutByte(max_chunk & 0xff);
99 bw_.PutBytes(ptr, size);
100 ptr += size;
101 data_size -= size;
102 seq += 1;
103 }
104 return true;
105 }
106
WriteXMP(const std::string & data)107 bool Encoder::WriteXMP(const std::string& data) {
108 if (data.size() == 0) return true;
109 const uint8_t kXMP[] = "http://ns.adobe.com/xap/1.0/";
110 const size_t kXMP_size = 29;
111 const size_t data_size = 2 + data.size() + kXMP_size;
112 if (data_size > 0xffff) return false; // error
113 ok_ = ok_ && bw_.Reserve(data_size + 2);
114 if (!ok_) return false;
115 bw_.PutByte(0xff);
116 bw_.PutByte(0xe1);
117 bw_.PutByte((data_size >> 8) & 0xff);
118 bw_.PutByte((data_size >> 0) & 0xff);
119 bw_.PutBytes(kXMP, kXMP_size);
120 bw_.PutBytes(reinterpret_cast<const uint8_t*>(data.data()), data.size());
121 return true;
122 }
123
WriteDQT()124 void Encoder::WriteDQT() {
125 const size_t data_size = 2 * 65 + 2;
126 const uint8_t kDQTHeader[] = { 0xff, 0xdb, 0x00, (uint8_t)data_size };
127 ok_ = ok_ && bw_.Reserve(data_size + 2);
128 if (!ok_) return;
129 bw_.PutBytes(kDQTHeader, sizeof(kDQTHeader));
130 for (int n = 0; n <= 1; ++n) {
131 bw_.PutByte(n);
132 const uint8_t* quant = quants_[n].quant_;
133 for (int i = 0; i < 64; ++i) {
134 bw_.PutByte(quant[kZigzag[i]]);
135 }
136 }
137 }
138
139 ////////////////////////////////////////////////////////////////////////////////
140
141 #define DATA_16b(X) ((uint8_t)((X) >> 8)), ((uint8_t)((X) & 0xff))
142
WriteSOF()143 void Encoder::WriteSOF() { // SOF
144 const size_t data_size = 3 * nb_comps_ + 8;
145 assert(data_size <= 255);
146 const uint8_t kHeader[] = {
147 0xff, 0xc0, DATA_16b(data_size), // SOF0 marker, size
148 0x08, // 8bits/components
149 DATA_16b(H_), DATA_16b(W_), // height, width
150 (uint8_t)nb_comps_ // number of components
151 };
152 ok_ = ok_ && bw_.Reserve(data_size + 2);
153 if (!ok_) return;
154 bw_.PutBytes(kHeader, sizeof(kHeader));
155 for (int c = 0; c < nb_comps_; ++c) {
156 bw_.PutByte(c + 1);
157 bw_.PutByte(block_dims_[c]);
158 bw_.PutByte(quant_idx_[c]);
159 }
160 }
161
WriteDHT()162 void Encoder::WriteDHT() {
163 InitCodes(false);
164 const int nb_tables = (nb_comps_ == 1 ? 1 : 2);
165 for (int c = 0; c < nb_tables; ++c) { // luma, chroma
166 for (int type = 0; type <= 1; ++type) { // dc, ac
167 const HuffmanTable* const h = Huffman_tables_[type * 2 + c];
168 const size_t data_size = 3 + 16 + h->nb_syms_;
169 assert(data_size <= 255);
170 ok_ = ok_ && bw_.Reserve(data_size + 2);
171 if (!ok_) return;
172 bw_.PutByte(0xff);
173 bw_.PutByte(0xc4);
174 bw_.PutByte(0x00 /*data_size >> 8*/);
175 bw_.PutByte(data_size);
176 bw_.PutByte((type << 4) | c);
177 bw_.PutBytes(h->bits_, 16);
178 bw_.PutBytes(h->syms_, h->nb_syms_);
179 }
180 }
181 }
182
183 ////////////////////////////////////////////////////////////////////////////////
184
WriteSOS()185 void Encoder::WriteSOS() { // SOS
186 const size_t data_size = 3 + nb_comps_ * 2 + 3;
187 assert(data_size <= 255);
188 const uint8_t kHeader[] = {
189 0xff, 0xda, DATA_16b(data_size), (uint8_t)nb_comps_
190 };
191 ok_ = ok_ && bw_.Reserve(data_size + 2);
192 if (!ok_) return;
193 bw_.PutBytes(kHeader, sizeof(kHeader));
194 for (int c = 0; c < nb_comps_; ++c) {
195 bw_.PutByte(c + 1);
196 bw_.PutByte(quant_idx_[c] * 0x11);
197 }
198 bw_.PutByte(0x00); // Ss
199 bw_.PutByte(0x3f); // Se
200 bw_.PutByte(0x00); // Ah/Al
201 }
202
203 ////////////////////////////////////////////////////////////////////////////////
204
WriteEOI()205 void Encoder::WriteEOI() { // EOI
206 if (ok_) bw_.Flush();
207 ok_ = ok_ && bw_.Reserve(2);
208 if (!ok_) return;
209 // append EOI
210 bw_.PutByte(0xff);
211 bw_.PutByte(0xd9);
212 }
213
214 ////////////////////////////////////////////////////////////////////////////////
215
216 #undef DATA_16b
217
218 } // namespace sjpeg
219