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 */
29namespace jpegutil {
30
31class Transform;
32class Plane;
33
34inline int sgn(int val) { return (0 < val) - (val < 0); }
35
36inline int min(int a, int b) { return a < b ? a : b; }
37
38inline 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 */
47class 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
55  inline int output_width() const { return output_width_; }
56
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 */
87struct 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 */
104template <unsigned int ROWS>
105class 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 */
144int 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 */
153int 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
172template <unsigned int ROWS>
173jpegutil::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
180template <unsigned int ROWS>
181const 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