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 struct 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 (unsigned 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 (unsigned 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