1 /*
2 * Copyright (C) 2014 The Android Open Source Project
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 #pragma once
17
18 #include "math.h"
19 #include <array>
20 #include <cassert>
21 #include <functional>
22 #include <memory>
23 #include <stdlib.h>
24 #include <vector>
25
26 /*
27 * Provides a wrapper around libjpeg.
28 */
29 namespace jpegutil {
30
31 class Transform;
32 class Plane;
33
sgn(int val)34 inline int sgn(int val) { return (0 < val) - (val < 0); }
35
min(int a,int b)36 inline int min(int a, int b) { return a < b ? a : b; }
37
max(int a,int b)38 inline int max(int a, int b) { return a > b ? a : b; }
39
40 /**
41 * Represents a combined cropping and rotation transformation.
42 *
43 * The transformation maps the coordinates (orig_x, orig_y) and (one_x, one_y)
44 * in the input image to the origin and (output_width, output_height)
45 * respectively.
46 */
47 class Transform {
48 public:
49 Transform(int orig_x, int orig_y, int one_x, int one_y);
50
51 static Transform ForCropFollowedByRotation(int cropLeft, int cropTop,
52 int cropRight, int cropBottom,
53 int rot90);
54
output_width()55 inline int output_width() const { return output_width_; }
56
output_height()57 inline int output_height() const { return output_height_; }
58
59 bool operator==(const Transform& other) const;
60
61 /**
62 * Transforms the input coordinates. Coordinates outside the cropped region
63 * are clamped to valid values.
64 */
65 void Map(int x, int y, int* x_out, int* y_out) const;
66
67 private:
68 int output_width_;
69 int output_height_;
70
71 // The coordinates of the point to map the origin to.
72 const int orig_x_, orig_y_;
73 // The coordinates of the point to map the point (output_width(),
74 // output_height()) to.
75 const int one_x_, one_y_;
76
77 // A matrix for the rotational component.
78 int mat00_, mat01_;
79 int mat10_, mat11_;
80 };
81
82 /**
83 * Represents a model for accessing pixel data for a single plane of an image.
84 * Note that the actual data is not owned by this class, and the underlying
85 * data does not need to be stored in separate planes.
86 */
87 struct Plane {
88 // The dimensions of this plane of the image
89 int width;
90 int height;
91
92 // A pointer to raw pixel data
93 const unsigned char* data;
94 // The difference in address between consecutive pixels in the same row
95 int pixel_stride;
96 // The difference in address between the start of consecutive rows
97 int row_stride;
98 };
99
100 /**
101 * Provides an interface for simultaneously reading a certain number of rows of
102 * an image plane as contiguous arrays, suitable for use with libjpeg.
103 */
104 template <unsigned int ROWS>
105 class RowIterator {
106 public:
107 /**
108 * Creates a new RowIterator which will crop and rotate with the given
109 * transform.
110 *
111 * @param plane the plane to iterate over
112 * @param transform the transformation to map output values into the
113 * coordinate space of the plane
114 * @param row_length the length of the rows returned via LoadAt(). If this is
115 * longer than the width of the output (after applying the transform), then
116 * the right-most value is repeated.
117 */
118 inline RowIterator(Plane plane, Transform transform, int row_length);
119
120 /**
121 * Returns an array of pointers into consecutive rows of contiguous image
122 * data starting at y. That is, samples within each row are contiguous.
123 * However, the individual arrays pointed-to may be separate.
124 * When the end of the image is reached, the last row of the image is
125 * repeated.
126 * The returned pointers are valid until the next call to LoadAt().
127 */
128 inline const std::array<unsigned char*, ROWS> LoadAt(int y_base);
129
130 private:
131 Plane plane_;
132 Transform transform_;
133 // The length of a row, with padding to the next multiple of 64.
134 int padded_row_length_;
135 std::vector<unsigned char> buf_;
136 };
137
138 /**
139 * Compresses an image from YUV 420p to JPEG. Output is buffered in outBuf until
140 * capacity is reached, at which point flush(size_t) is called to write
141 * out the specified number of bytes from outBuf. Returns the number of bytes
142 * written, or -1 in case of an error.
143 */
144 int Compress(int img_width, int img_height, RowIterator<16>& y_row_generator,
145 RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator,
146 unsigned char* out_buf, size_t out_buf_capacity,
147 std::function<void(size_t)> flush, int quality);
148
149 /**
150 * Compresses an image from YUV 420p to JPEG. Output is written into outBuf.
151 * Returns the number of bytes written, or -1 in case of an error.
152 */
153 int Compress(
154 /** Input image dimensions */
155 int width, int height,
156 /** Y Plane */
157 unsigned char* yBuf, int yPStride, int yRStride,
158 /** Cb Plane */
159 unsigned char* cbBuf, int cbPStride, int cbRStride,
160 /** Cr Plane */
161 unsigned char* crBuf, int crPStride, int crRStride,
162 /** Output */
163 unsigned char* outBuf, size_t outBufCapacity,
164 /** Jpeg compression parameters */
165 int quality,
166 /** Crop */
167 int cropLeft, int cropTop, int cropRight, int cropBottom,
168 /** Rotation */
169 int rot90);
170 }
171
172 template <unsigned int ROWS>
RowIterator(Plane plane,Transform transform,int row_length)173 jpegutil::RowIterator<ROWS>::RowIterator(Plane plane, Transform transform,
174 int row_length)
175 : plane_(plane), transform_(transform) {
176 padded_row_length_ = row_length;
177 buf_ = std::vector<unsigned char>(row_length * ROWS);
178 }
179
180 template <unsigned int ROWS>
LoadAt(int y_base)181 const std::array<unsigned char*, ROWS> jpegutil::RowIterator<ROWS>::LoadAt(
182 int y_base) {
183 std::array<unsigned char*, ROWS> buf_ptrs;
184 for (int i = 0; i < ROWS; i++) {
185 buf_ptrs[i] = &buf_[padded_row_length_ * i];
186 }
187
188 if (plane_.width == 0 || plane_.height == 0) {
189 return buf_ptrs;
190 }
191
192 for (int i = 0; i < ROWS; i++) {
193 int y = i + y_base;
194 y = min(y, transform_.output_height() - 1);
195
196 int output_width = padded_row_length_;
197 output_width = min(output_width, transform_.output_width());
198 output_width = min(output_width, plane_.width);
199
200 // Each row in the output image will be copied into buf_ by gathering pixels
201 // along an axis-aligned line in the plane.
202 // The line is defined by (startX, startY) -> (endX, endY), computed via the
203 // current Transform.
204 int startX;
205 int startY;
206 transform_.Map(0, y, &startX, &startY);
207
208 int endX;
209 int endY;
210 transform_.Map(output_width - 1, y, &endX, &endY);
211
212 // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane.
213 startX = min(startX, plane_.width - 1);
214 startY = min(startY, plane_.height - 1);
215 endX = min(endX, plane_.width - 1);
216 endY = min(endY, plane_.height - 1);
217 startX = max(startX, 0);
218 startY = max(startY, 0);
219 endX = max(endX, 0);
220 endY = max(endY, 0);
221
222 // To reduce work inside the copy-loop, precompute the start, end, and
223 // stride relating the values to be gathered from plane_ into buf
224 // for this particular scan-line.
225 int dx = sgn(endX - startX);
226 int dy = sgn(endY - startY);
227 assert(dx == 0 || dy == 0);
228 // The index into plane_.data of (startX, startY)
229 int plane_start = startX * plane_.pixel_stride + startY * plane_.row_stride;
230 // The index into plane_.data of (endX, endY)
231 int plane_end = endX * plane_.pixel_stride + endY * plane_.row_stride;
232 // The stride, in terms of indices in plane_data, required to enumerate the
233 // samples between the start and end points.
234 int stride = dx * plane_.pixel_stride + dy * plane_.row_stride;
235 // In the degenerate-case of a 1x1 plane, startX and endX are equal, so
236 // stride would be 0, resulting in an infinite-loop. To avoid this case,
237 // use a stride of at-least 1.
238 if (stride == 0) {
239 stride = 1;
240 }
241
242 int outX = 0;
243 for (int idx = plane_start; idx >= min(plane_start, plane_end) &&
244 idx <= max(plane_start, plane_end);
245 idx += stride) {
246 buf_ptrs[i][outX] = plane_.data[idx];
247 outX++;
248 }
249
250 // Fill the remaining right-edge of the buffer by extending the last
251 // value.
252 unsigned char right_padding_value = buf_ptrs[i][outX - 1];
253 // TODO OPTIMIZE Use memset instead.
254 for (; outX < padded_row_length_; outX++) {
255 buf_ptrs[i][outX] = right_padding_value;
256 }
257 }
258
259 return buf_ptrs;
260 }
261