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 34 inline int sgn(int val) { return (0 < val) - (val < 0); } 35 36 inline int min(int a, int b) { return a < b ? a : b; } 37 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 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 */ 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> 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> 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