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