/src/guetzli/guetzli/output_image.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2016 Google Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include "guetzli/output_image.h" |
18 | | |
19 | | #include <algorithm> |
20 | | #include <assert.h> |
21 | | #include <stdio.h> |
22 | | #include <string.h> |
23 | | #include <cmath> |
24 | | #include <cstdlib> |
25 | | |
26 | | #include "guetzli/idct.h" |
27 | | #include "guetzli/color_transform.h" |
28 | | #include "guetzli/dct_double.h" |
29 | | #include "guetzli/gamma_correct.h" |
30 | | #include "guetzli/preprocess_downsample.h" |
31 | | #include "guetzli/quantize.h" |
32 | | |
33 | | namespace guetzli { |
34 | | |
35 | | OutputImageComponent::OutputImageComponent(int w, int h) |
36 | 8.12k | : width_(w), height_(h) { |
37 | 8.12k | Reset(1, 1); |
38 | 8.12k | } |
39 | | |
40 | 134k | void OutputImageComponent::Reset(int factor_x, int factor_y) { |
41 | 134k | factor_x_ = factor_x; |
42 | 134k | factor_y_ = factor_y; |
43 | 134k | width_in_blocks_ = (width_ + 8 * factor_x_ - 1) / (8 * factor_x_); |
44 | 134k | height_in_blocks_ = (height_ + 8 * factor_y_ - 1) / (8 * factor_y_); |
45 | 134k | num_blocks_ = width_in_blocks_ * height_in_blocks_; |
46 | 134k | coeffs_ = std::vector<coeff_t>(num_blocks_ * kDCTBlockSize); |
47 | 134k | pixels_ = std::vector<uint16_t>(width_ * height_, 128 << 4); |
48 | 8.76M | for (int i = 0; i < kDCTBlockSize; ++i) quant_[i] = 1; |
49 | 134k | } |
50 | | |
51 | 194k | bool OutputImageComponent::IsAllZero() const { |
52 | 194k | int numcoeffs = num_blocks_ * kDCTBlockSize; |
53 | 69.8M | for (int i = 0; i < numcoeffs; ++i) { |
54 | 69.8M | if (coeffs_[i] != 0) return false; |
55 | 69.8M | } |
56 | 68.6k | return true; |
57 | 194k | } |
58 | | |
59 | | void OutputImageComponent::GetCoeffBlock(int block_x, int block_y, |
60 | 5.50M | coeff_t block[kDCTBlockSize]) const { |
61 | 5.50M | assert(block_x < width_in_blocks_); |
62 | 5.50M | assert(block_y < height_in_blocks_); |
63 | 5.50M | int offset = (block_y * width_in_blocks_ + block_x) * kDCTBlockSize; |
64 | 5.50M | memcpy(block, &coeffs_[offset], kDCTBlockSize * sizeof(coeffs_[0])); |
65 | 5.50M | } |
66 | | |
67 | | void OutputImageComponent::ToPixels(int xmin, int ymin, int xsize, int ysize, |
68 | 18.1M | uint8_t* out, int stride) const { |
69 | 18.1M | assert(xmin >= 0); |
70 | 18.1M | assert(ymin >= 0); |
71 | 18.1M | assert(xmin < width_); |
72 | 18.1M | assert(ymin < height_); |
73 | 18.1M | const int yend1 = ymin + ysize; |
74 | 18.1M | const int yend0 = std::min(yend1, height_); |
75 | 18.1M | int y = ymin; |
76 | 175M | for (; y < yend0; ++y) { |
77 | 157M | const int xend1 = xmin + xsize; |
78 | 157M | const int xend0 = std::min(xend1, width_); |
79 | 157M | int x = xmin; |
80 | 157M | int px = y * width_ + xmin; |
81 | 2.35G | for (; x < xend0; ++x, ++px, out += stride) { |
82 | 2.19G | *out = static_cast<uint8_t>((pixels_[px] + 8 - (x & 1)) >> 4); |
83 | 2.19G | } |
84 | 157M | const int offset = -stride; |
85 | 204M | for (; x < xend1; ++x) { |
86 | 46.9M | *out = out[offset]; |
87 | 46.9M | out += stride; |
88 | 46.9M | } |
89 | 157M | } |
90 | 23.1M | for (; y < yend1; ++y) { |
91 | 5.06M | const int offset = -stride * xsize; |
92 | 45.6M | for (int x = 0; x < xsize; ++x) { |
93 | 40.5M | *out = out[offset]; |
94 | 40.5M | out += stride; |
95 | 40.5M | } |
96 | 5.06M | } |
97 | 18.1M | } |
98 | | |
99 | 0 | void OutputImageComponent::ToFloatPixels(float* out, int stride) const { |
100 | 0 | assert(factor_x_ == 1); |
101 | 0 | assert(factor_y_ == 1); |
102 | 0 | for (int block_y = 0; block_y < height_in_blocks_; ++block_y) { |
103 | 0 | for (int block_x = 0; block_x < width_in_blocks_; ++block_x) { |
104 | 0 | coeff_t block[kDCTBlockSize]; |
105 | 0 | GetCoeffBlock(block_x, block_y, block); |
106 | 0 | double blockd[kDCTBlockSize]; |
107 | 0 | for (int k = 0; k < kDCTBlockSize; ++k) { |
108 | 0 | blockd[k] = block[k]; |
109 | 0 | } |
110 | 0 | ComputeBlockIDCTDouble(blockd); |
111 | 0 | for (int iy = 0; iy < 8; ++iy) { |
112 | 0 | for (int ix = 0; ix < 8; ++ix) { |
113 | 0 | int y = block_y * 8 + iy; |
114 | 0 | int x = block_x * 8 + ix; |
115 | 0 | if (y >= height_ || x >= width_) continue; |
116 | 0 | out[(y * width_ + x) * stride] = static_cast<float>(blockd[8 * iy + ix] + 128.0); |
117 | 0 | } |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | void OutputImageComponent::SetCoeffBlock(int block_x, int block_y, |
124 | 19.0M | const coeff_t block[kDCTBlockSize]) { |
125 | 19.0M | assert(block_x < width_in_blocks_); |
126 | 19.0M | assert(block_y < height_in_blocks_); |
127 | 19.0M | int offset = (block_y * width_in_blocks_ + block_x) * kDCTBlockSize; |
128 | 19.0M | memcpy(&coeffs_[offset], block, kDCTBlockSize * sizeof(coeffs_[0])); |
129 | 19.0M | uint8_t idct[kDCTBlockSize]; |
130 | 19.0M | ComputeBlockIDCT(&coeffs_[offset], idct); |
131 | 19.0M | UpdatePixelsForBlock(block_x, block_y, idct); |
132 | 19.0M | } |
133 | | |
134 | | void OutputImageComponent::UpdatePixelsForBlock( |
135 | 19.0M | int block_x, int block_y, const uint8_t idct[kDCTBlockSize]) { |
136 | 19.0M | if (factor_x_ == 1 && factor_y_ == 1) { |
137 | 151M | for (int iy = 0; iy < 8; ++iy) { |
138 | 1.21G | for (int ix = 0; ix < 8; ++ix) { |
139 | 1.07G | int x = 8 * block_x + ix; |
140 | 1.07G | int y = 8 * block_y + iy; |
141 | 1.07G | if (x >= width_ || y >= height_) continue; |
142 | 999M | int p = y * width_ + x; |
143 | 999M | pixels_[p] = idct[8 * iy + ix] << 4; |
144 | 999M | } |
145 | 134M | } |
146 | 16.8M | } else if (factor_x_ == 2 && factor_y_ == 2) { |
147 | | // Fill in the 10x10 pixel area in the subsampled image that will be the |
148 | | // basis of the upsampling. This area is enough to hold the 3x3 kernel of |
149 | | // the fancy upsampler around each pixel. |
150 | 2.16M | static const int kSubsampledEdgeSize = 10; |
151 | 2.16M | uint16_t subsampled[kSubsampledEdgeSize * kSubsampledEdgeSize]; |
152 | 23.7M | for (int j = 0; j < kSubsampledEdgeSize; ++j) { |
153 | | // The order we fill in the rows is: |
154 | | // 8 rows intersecting the block, row below, row above |
155 | 21.6M | const int y0 = block_y * 16 + (j < 9 ? j * 2 : -2); |
156 | 237M | for (int i = 0; i < kSubsampledEdgeSize; ++i) { |
157 | | // The order we fill in each row is: |
158 | | // 8 pixels within the block, left edge, right edge |
159 | 216M | const int ix = ((j < 9 ? (j + 1) * kSubsampledEdgeSize : 0) + |
160 | 216M | (i < 9 ? i + 1 : 0)); |
161 | 216M | const int x0 = block_x * 16 + (i < 9 ? i * 2 : -2); |
162 | 216M | if (x0 < 0) { |
163 | 5.30M | subsampled[ix] = subsampled[ix + 1]; |
164 | 211M | } else if (y0 < 0) { |
165 | 6.20M | subsampled[ix] = subsampled[ix + kSubsampledEdgeSize]; |
166 | 204M | } else if (x0 >= width_) { |
167 | 24.1M | subsampled[ix] = subsampled[ix - 1]; |
168 | 180M | } else if (y0 >= height_) { |
169 | 24.5M | subsampled[ix] = subsampled[ix - kSubsampledEdgeSize]; |
170 | 156M | } else if (i < 8 && j < 8) { |
171 | 106M | subsampled[ix] = idct[j * 8 + i] << 4; |
172 | 106M | } else { |
173 | | // Reconstruct the subsampled pixels around the edge of the current |
174 | | // block by computing the inverse of the fancy upsampler. |
175 | 49.5M | const int y1 = std::max(y0 - 1, 0); |
176 | 49.5M | const int x1 = std::max(x0 - 1, 0); |
177 | 49.5M | subsampled[ix] = (pixels_[y0 * width_ + x0] * 9 + |
178 | 49.5M | pixels_[y1 * width_ + x1] + |
179 | 49.5M | pixels_[y0 * width_ + x1] * -3 + |
180 | 49.5M | pixels_[y1 * width_ + x0] * -3) >> 2; |
181 | 49.5M | } |
182 | 216M | } |
183 | 21.6M | } |
184 | | |
185 | | // Determine area to update. |
186 | 2.16M | int xmin = std::max(block_x * 16 - 1, 0); |
187 | 2.16M | int xmax = std::min(block_x * 16 + 16, width_ - 1); |
188 | 2.16M | int ymin = std::max(block_y * 16 - 1, 0); |
189 | 2.16M | int ymax = std::min(block_y * 16 + 16, height_ - 1); |
190 | | |
191 | | // Apply the fancy upsampler on the subsampled block. |
192 | 35.0M | for (int y = ymin; y <= ymax; ++y) { |
193 | 32.9M | const int y0 = ((y & ~1) / 2 - block_y * 8 + 1) * kSubsampledEdgeSize; |
194 | 32.9M | const int dy = ((y & 1) * 2 - 1) * kSubsampledEdgeSize; |
195 | 32.9M | uint16_t* rowptr = &pixels_[y * width_]; |
196 | 546M | for (int x = xmin; x <= xmax; ++x) { |
197 | 513M | const int x0 = (x & ~1) / 2 - block_x * 8 + 1; |
198 | 513M | const int dx = (x & 1) * 2 - 1; |
199 | 513M | const int ix = x0 + y0; |
200 | 513M | rowptr[x] = (subsampled[ix] * 9 + subsampled[ix + dy] * 3 + |
201 | 513M | subsampled[ix + dx] * 3 + subsampled[ix + dx + dy]) >> 4; |
202 | 513M | } |
203 | 32.9M | } |
204 | 2.16M | } else { |
205 | 0 | printf("Sampling ratio not supported: factor_x = %d factor_y = %d\n", |
206 | 0 | factor_x_, factor_y_); |
207 | 0 | exit(1); |
208 | 0 | } |
209 | 19.0M | } |
210 | | |
211 | | void OutputImageComponent::CopyFromJpegComponent(const JPEGComponent& comp, |
212 | | int factor_x, int factor_y, |
213 | 126k | const int* quant) { |
214 | 126k | Reset(factor_x, factor_y); |
215 | 126k | assert(width_in_blocks_ <= comp.width_in_blocks); |
216 | 126k | assert(height_in_blocks_ <= comp.height_in_blocks); |
217 | 126k | const size_t src_row_size = comp.width_in_blocks * kDCTBlockSize; |
218 | 786k | for (int block_y = 0; block_y < height_in_blocks_; ++block_y) { |
219 | 659k | const coeff_t* src_coeffs = &comp.coeffs[block_y * src_row_size]; |
220 | 4.30M | for (int block_x = 0; block_x < width_in_blocks_; ++block_x) { |
221 | 3.64M | coeff_t block[kDCTBlockSize]; |
222 | 237M | for (int i = 0; i < kDCTBlockSize; ++i) { |
223 | 233M | block[i] = src_coeffs[i] * quant[i]; |
224 | 233M | } |
225 | 3.64M | SetCoeffBlock(block_x, block_y, block); |
226 | 3.64M | src_coeffs += kDCTBlockSize; |
227 | 3.64M | } |
228 | 659k | } |
229 | 126k | memcpy(quant_, quant, sizeof(quant_)); |
230 | 126k | } |
231 | | |
232 | 126k | void OutputImageComponent::ApplyGlobalQuantization(const int q[kDCTBlockSize]) { |
233 | 740k | for (int block_y = 0; block_y < height_in_blocks_; ++block_y) { |
234 | 3.81M | for (int block_x = 0; block_x < width_in_blocks_; ++block_x) { |
235 | 3.20M | coeff_t block[kDCTBlockSize]; |
236 | 3.20M | GetCoeffBlock(block_x, block_y, block); |
237 | 3.20M | if (QuantizeBlock(block, q)) { |
238 | 1.11M | SetCoeffBlock(block_x, block_y, block); |
239 | 1.11M | } |
240 | 3.20M | } |
241 | 613k | } |
242 | 126k | memcpy(quant_, q, sizeof(quant_)); |
243 | 126k | } |
244 | | |
245 | | OutputImage::OutputImage(int w, int h) |
246 | 8.12k | : width_(w), |
247 | 8.12k | height_(h), |
248 | 8.12k | components_(3, OutputImageComponent(w, h)) {} |
249 | | |
250 | 50.3k | void OutputImage::CopyFromJpegData(const JPEGData& jpg) { |
251 | 177k | for (size_t i = 0; i < jpg.components.size(); ++i) { |
252 | 126k | const JPEGComponent& comp = jpg.components[i]; |
253 | 126k | assert(jpg.max_h_samp_factor % comp.h_samp_factor == 0); |
254 | 126k | assert(jpg.max_v_samp_factor % comp.v_samp_factor == 0); |
255 | 126k | int factor_x = jpg.max_h_samp_factor / comp.h_samp_factor; |
256 | 126k | int factor_y = jpg.max_v_samp_factor / comp.v_samp_factor; |
257 | 126k | assert(comp.quant_idx < jpg.quant.size()); |
258 | 126k | components_[i].CopyFromJpegComponent(comp, factor_x, factor_y, |
259 | 126k | &jpg.quant[comp.quant_idx].values[0]); |
260 | 126k | } |
261 | 50.3k | } |
262 | | |
263 | | namespace { |
264 | | |
265 | | void SetDownsampledCoefficients(const std::vector<float>& pixels, |
266 | | int factor_x, int factor_y, |
267 | 0 | OutputImageComponent* comp) { |
268 | 0 | assert(pixels.size() == comp->width() * comp->height()); |
269 | 0 | comp->Reset(factor_x, factor_y); |
270 | 0 | for (int block_y = 0; block_y < comp->height_in_blocks(); ++block_y) { |
271 | 0 | for (int block_x = 0; block_x < comp->width_in_blocks(); ++block_x) { |
272 | 0 | double blockd[kDCTBlockSize]; |
273 | 0 | int x0 = 8 * block_x * factor_x; |
274 | 0 | int y0 = 8 * block_y * factor_y; |
275 | 0 | assert(x0 < comp->width()); |
276 | 0 | assert(y0 < comp->height()); |
277 | 0 | for (int iy = 0; iy < 8; ++iy) { |
278 | 0 | for (int ix = 0; ix < 8; ++ix) { |
279 | 0 | float avg = 0.0; |
280 | 0 | for (int j = 0; j < factor_y; ++j) { |
281 | 0 | for (int i = 0; i < factor_x; ++i) { |
282 | 0 | int x = std::min(x0 + ix * factor_x + i, comp->width() - 1); |
283 | 0 | int y = std::min(y0 + iy * factor_y + j, comp->height() - 1); |
284 | 0 | avg += pixels[y * comp->width() + x]; |
285 | 0 | } |
286 | 0 | } |
287 | 0 | avg /= factor_x * factor_y; |
288 | 0 | blockd[iy * 8 + ix] = avg; |
289 | 0 | } |
290 | 0 | } |
291 | 0 | ComputeBlockDCTDouble(blockd); |
292 | 0 | blockd[0] -= 1024.0; |
293 | 0 | coeff_t block[kDCTBlockSize]; |
294 | 0 | for (int k = 0; k < kDCTBlockSize; ++k) { |
295 | 0 | block[k] = static_cast<coeff_t>(std::round(blockd[k])); |
296 | 0 | } |
297 | 0 | comp->SetCoeffBlock(block_x, block_y, block); |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | } // namespace |
303 | | |
304 | 0 | void OutputImage::Downsample(const DownsampleConfig& cfg) { |
305 | 0 | if (components_[1].IsAllZero() && components_[2].IsAllZero()) { |
306 | | // If the image is already grayscale, nothing to do. |
307 | 0 | return; |
308 | 0 | } |
309 | 0 | if (cfg.use_silver_screen && |
310 | 0 | cfg.u_factor_x == 2 && cfg.u_factor_y == 2 && |
311 | 0 | cfg.v_factor_x == 2 && cfg.v_factor_y == 2) { |
312 | 0 | std::vector<uint8_t> rgb = ToSRGB(); |
313 | 0 | std::vector<std::vector<float> > yuv = RGBToYUV420(rgb, width_, height_); |
314 | 0 | SetDownsampledCoefficients(yuv[0], 1, 1, &components_[0]); |
315 | 0 | SetDownsampledCoefficients(yuv[1], 2, 2, &components_[1]); |
316 | 0 | SetDownsampledCoefficients(yuv[2], 2, 2, &components_[2]); |
317 | 0 | return; |
318 | 0 | } |
319 | | // Get the floating-point precision YUV array represented by the set of |
320 | | // DCT coefficients. |
321 | 0 | std::vector<std::vector<float> > yuv(3, std::vector<float>(width_ * height_)); |
322 | 0 | for (int c = 0; c < 3; ++c) { |
323 | 0 | components_[c].ToFloatPixels(&yuv[c][0], 1); |
324 | 0 | } |
325 | |
|
326 | 0 | yuv = PreProcessChannel(width_, height_, 2, 1.3f, 0.5f, |
327 | 0 | cfg.u_sharpen, cfg.u_blur, yuv); |
328 | 0 | yuv = PreProcessChannel(width_, height_, 1, 1.3f, 0.5f, |
329 | 0 | cfg.v_sharpen, cfg.v_blur, yuv); |
330 | | |
331 | | // Do the actual downsampling (averaging) and forward-DCT. |
332 | 0 | if (cfg.u_factor_x != 1 || cfg.u_factor_y != 1) { |
333 | 0 | SetDownsampledCoefficients(yuv[1], cfg.u_factor_x, cfg.u_factor_y, |
334 | 0 | &components_[1]); |
335 | 0 | } |
336 | 0 | if (cfg.v_factor_x != 1 || cfg.v_factor_y != 1) { |
337 | 0 | SetDownsampledCoefficients(yuv[2], cfg.v_factor_x, cfg.v_factor_y, |
338 | 0 | &components_[2]); |
339 | 0 | } |
340 | 0 | } |
341 | | |
342 | 42.1k | void OutputImage::ApplyGlobalQuantization(const int q[3][kDCTBlockSize]) { |
343 | 168k | for (int c = 0; c < 3; ++c) { |
344 | 126k | components_[c].ApplyGlobalQuantization(&q[c][0]); |
345 | 126k | } |
346 | 42.1k | } |
347 | | |
348 | 157k | void OutputImage::SaveToJpegData(JPEGData* jpg) const { |
349 | 157k | assert(components_[0].factor_x() == 1); |
350 | 157k | assert(components_[0].factor_y() == 1); |
351 | 157k | jpg->width = width_; |
352 | 157k | jpg->height = height_; |
353 | 157k | jpg->max_h_samp_factor = 1; |
354 | 157k | jpg->max_v_samp_factor = 1; |
355 | 157k | jpg->MCU_cols = components_[0].width_in_blocks(); |
356 | 157k | jpg->MCU_rows = components_[0].height_in_blocks(); |
357 | 157k | int ncomp = components_[1].IsAllZero() && components_[2].IsAllZero() ? 1 : 3; |
358 | 409k | for (int i = 1; i < ncomp; ++i) { |
359 | 251k | jpg->max_h_samp_factor = std::max(jpg->max_h_samp_factor, |
360 | 251k | components_[i].factor_x()); |
361 | 251k | jpg->max_v_samp_factor = std::max(jpg->max_h_samp_factor, |
362 | 251k | components_[i].factor_y()); |
363 | 251k | jpg->MCU_cols = std::min(jpg->MCU_cols, components_[i].width_in_blocks()); |
364 | 251k | jpg->MCU_rows = std::min(jpg->MCU_rows, components_[i].height_in_blocks()); |
365 | 251k | } |
366 | 157k | jpg->components.resize(ncomp); |
367 | 157k | int q[3][kDCTBlockSize]; |
368 | 629k | for (int c = 0; c < 3; ++c) { |
369 | 472k | memcpy(&q[c][0], components_[c].quant(), kDCTBlockSize * sizeof(q[0][0])); |
370 | 472k | } |
371 | 566k | for (int c = 0; c < ncomp; ++c) { |
372 | 409k | JPEGComponent* comp = &jpg->components[c]; |
373 | 409k | assert(jpg->max_h_samp_factor % components_[c].factor_x() == 0); |
374 | 409k | assert(jpg->max_v_samp_factor % components_[c].factor_y() == 0); |
375 | 409k | comp->id = c; |
376 | 409k | comp->h_samp_factor = jpg->max_h_samp_factor / components_[c].factor_x(); |
377 | 409k | comp->v_samp_factor = jpg->max_v_samp_factor / components_[c].factor_y(); |
378 | 409k | comp->width_in_blocks = jpg->MCU_cols * comp->h_samp_factor; |
379 | 409k | comp->height_in_blocks = jpg->MCU_rows * comp->v_samp_factor; |
380 | 409k | comp->num_blocks = comp->width_in_blocks * comp->height_in_blocks; |
381 | 409k | comp->coeffs.resize(kDCTBlockSize * comp->num_blocks); |
382 | | |
383 | 409k | int last_dc = 0; |
384 | 409k | const coeff_t* src_coeffs = components_[c].coeffs(); |
385 | 409k | coeff_t* dest_coeffs = &comp->coeffs[0]; |
386 | 2.47M | for (int block_y = 0; block_y < comp->height_in_blocks; ++block_y) { |
387 | 15.7M | for (int block_x = 0; block_x < comp->width_in_blocks; ++block_x) { |
388 | 13.7M | if (block_y >= components_[c].height_in_blocks() || |
389 | 13.7M | block_x >= components_[c].width_in_blocks()) { |
390 | 728k | dest_coeffs[0] = last_dc; |
391 | 46.5M | for (int k = 1; k < kDCTBlockSize; ++k) { |
392 | 45.8M | dest_coeffs[k] = 0; |
393 | 45.8M | } |
394 | 13.0M | } else { |
395 | 845M | for (int k = 0; k < kDCTBlockSize; ++k) { |
396 | 832M | const int quant = q[c][k]; |
397 | 832M | int coeff = src_coeffs[k]; |
398 | 832M | assert(coeff % quant == 0); |
399 | 832M | dest_coeffs[k] = coeff / quant; |
400 | 832M | } |
401 | 13.0M | src_coeffs += kDCTBlockSize; |
402 | 13.0M | } |
403 | 13.7M | last_dc = dest_coeffs[0]; |
404 | 13.7M | dest_coeffs += kDCTBlockSize; |
405 | 13.7M | } |
406 | 2.06M | } |
407 | 409k | } |
408 | 157k | SaveQuantTables(q, jpg); |
409 | 157k | } |
410 | | |
411 | | std::vector<uint8_t> OutputImage::ToSRGB(int xmin, int ymin, |
412 | 6.04M | int xsize, int ysize) const { |
413 | 6.04M | std::vector<uint8_t> rgb(xsize * ysize * 3); |
414 | 24.1M | for (int c = 0; c < 3; ++c) { |
415 | 18.1M | components_[c].ToPixels(xmin, ymin, xsize, ysize, &rgb[c], 3); |
416 | 18.1M | } |
417 | 767M | for (size_t p = 0; p < rgb.size(); p += 3) { |
418 | 761M | ColorTransformYCbCrToRGB(&rgb[p]); |
419 | 761M | } |
420 | 6.04M | return rgb; |
421 | 6.04M | } |
422 | | |
423 | 2.83k | std::vector<uint8_t> OutputImage::ToSRGB() const { |
424 | 2.83k | return ToSRGB(0, 0, width_, height_); |
425 | 2.83k | } |
426 | | |
427 | | void OutputImage::ToLinearRGB(int xmin, int ymin, int xsize, int ysize, |
428 | 6.03M | std::vector<std::vector<float> >* rgb) const { |
429 | 6.03M | const double* lut = Srgb8ToLinearTable(); |
430 | 6.03M | std::vector<uint8_t> rgb_pixels = ToSRGB(xmin, ymin, xsize, ysize); |
431 | 760M | for (int p = 0; p < xsize * ysize; ++p) { |
432 | 3.01G | for (int i = 0; i < 3; ++i) { |
433 | 2.26G | (*rgb)[i][p] = static_cast<float>(lut[rgb_pixels[3 * p + i]]); |
434 | 2.26G | } |
435 | 754M | } |
436 | 6.03M | } |
437 | | |
438 | 153k | void OutputImage::ToLinearRGB(std::vector<std::vector<float> >* rgb) const { |
439 | 153k | ToLinearRGB(0, 0, width_, height_, rgb); |
440 | 153k | } |
441 | | |
442 | 190k | std::string OutputImage::FrameTypeStr() const { |
443 | 190k | char buf[128]; |
444 | 190k | int len = snprintf(buf, sizeof(buf), "f%d%d%d%d%d%d", |
445 | 190k | component(0).factor_x(), component(0).factor_y(), |
446 | 190k | component(1).factor_x(), component(1).factor_y(), |
447 | 190k | component(2).factor_x(), component(2).factor_y()); |
448 | 190k | return std::string(buf, len); |
449 | 190k | } |
450 | | |
451 | | } // namespace guetzli |