| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #pragma once |
| |
| #include "math.h" |
| #include <array> |
| #include <cassert> |
| #include <functional> |
| #include <memory> |
| #include <stdlib.h> |
| #include <vector> |
| |
| /* |
| * Provides a wrapper around libjpeg. |
| */ |
| namespace jpegutil { |
| |
| class Transform; |
| class Plane; |
| |
| inline int sgn(int val) { return (0 < val) - (val < 0); } |
| |
| inline int min(int a, int b) { return a < b ? a : b; } |
| |
| inline int max(int a, int b) { return a > b ? a : b; } |
| |
| /** |
| * Represents a combined cropping and rotation transformation. |
| * |
| * The transformation maps the coordinates (orig_x, orig_y) and (one_x, one_y) |
| * in the input image to the origin and (output_width, output_height) |
| * respectively. |
| */ |
| class Transform { |
| public: |
| Transform(int orig_x, int orig_y, int one_x, int one_y); |
| |
| static Transform ForCropFollowedByRotation(int cropLeft, int cropTop, |
| int cropRight, int cropBottom, |
| int rot90); |
| |
| inline int output_width() const { return output_width_; } |
| |
| inline int output_height() const { return output_height_; } |
| |
| bool operator==(const Transform& other) const; |
| |
| /** |
| * Transforms the input coordinates. Coordinates outside the cropped region |
| * are clamped to valid values. |
| */ |
| void Map(int x, int y, int* x_out, int* y_out) const; |
| |
| private: |
| int output_width_; |
| int output_height_; |
| |
| // The coordinates of the point to map the origin to. |
| const int orig_x_, orig_y_; |
| // The coordinates of the point to map the point (output_width(), |
| // output_height()) to. |
| const int one_x_, one_y_; |
| |
| // A matrix for the rotational component. |
| int mat00_, mat01_; |
| int mat10_, mat11_; |
| }; |
| |
| /** |
| * Represents a model for accessing pixel data for a single plane of an image. |
| * Note that the actual data is not owned by this class, and the underlying |
| * data does not need to be stored in separate planes. |
| */ |
| struct Plane { |
| // The dimensions of this plane of the image |
| int width; |
| int height; |
| |
| // A pointer to raw pixel data |
| const unsigned char* data; |
| // The difference in address between consecutive pixels in the same row |
| int pixel_stride; |
| // The difference in address between the start of consecutive rows |
| int row_stride; |
| }; |
| |
| /** |
| * Provides an interface for simultaneously reading a certain number of rows of |
| * an image plane as contiguous arrays, suitable for use with libjpeg. |
| */ |
| template <unsigned int ROWS> |
| class RowIterator { |
| public: |
| /** |
| * Creates a new RowIterator which will crop and rotate with the given |
| * transform. |
| * |
| * @param plane the plane to iterate over |
| * @param transform the transformation to map output values into the |
| * coordinate space of the plane |
| * @param row_length the length of the rows returned via LoadAt(). If this is |
| * longer than the width of the output (after applying the transform), then |
| * the right-most value is repeated. |
| */ |
| inline RowIterator(Plane plane, Transform transform, int row_length); |
| |
| /** |
| * Returns an array of pointers into consecutive rows of contiguous image |
| * data starting at y. That is, samples within each row are contiguous. |
| * However, the individual arrays pointed-to may be separate. |
| * When the end of the image is reached, the last row of the image is |
| * repeated. |
| * The returned pointers are valid until the next call to LoadAt(). |
| */ |
| inline const std::array<unsigned char*, ROWS> LoadAt(int y_base); |
| |
| private: |
| Plane plane_; |
| Transform transform_; |
| // The length of a row, with padding to the next multiple of 64. |
| int padded_row_length_; |
| std::vector<unsigned char> buf_; |
| }; |
| |
| /** |
| * Compresses an image from YUV 420p to JPEG. Output is buffered in outBuf until |
| * capacity is reached, at which point flush(size_t) is called to write |
| * out the specified number of bytes from outBuf. Returns the number of bytes |
| * written, or -1 in case of an error. |
| */ |
| int Compress(int img_width, int img_height, RowIterator<16>& y_row_generator, |
| RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator, |
| unsigned char* out_buf, size_t out_buf_capacity, |
| std::function<void(size_t)> flush, int quality); |
| |
| /** |
| * Compresses an image from YUV 420p to JPEG. Output is written into outBuf. |
| * Returns the number of bytes written, or -1 in case of an error. |
| */ |
| int Compress( |
| /** Input image dimensions */ |
| int width, int height, |
| /** Y Plane */ |
| unsigned char* yBuf, int yPStride, int yRStride, |
| /** Cb Plane */ |
| unsigned char* cbBuf, int cbPStride, int cbRStride, |
| /** Cr Plane */ |
| unsigned char* crBuf, int crPStride, int crRStride, |
| /** Output */ |
| unsigned char* outBuf, size_t outBufCapacity, |
| /** Jpeg compression parameters */ |
| int quality, |
| /** Crop */ |
| int cropLeft, int cropTop, int cropRight, int cropBottom, |
| /** Rotation */ |
| int rot90); |
| } |
| |
| template <unsigned int ROWS> |
| jpegutil::RowIterator<ROWS>::RowIterator(Plane plane, Transform transform, |
| int row_length) |
| : plane_(plane), transform_(transform) { |
| padded_row_length_ = row_length; |
| buf_ = std::vector<unsigned char>(row_length * ROWS); |
| } |
| |
| template <unsigned int ROWS> |
| const std::array<unsigned char*, ROWS> jpegutil::RowIterator<ROWS>::LoadAt( |
| int y_base) { |
| std::array<unsigned char*, ROWS> buf_ptrs; |
| for (int i = 0; i < ROWS; i++) { |
| buf_ptrs[i] = &buf_[padded_row_length_ * i]; |
| } |
| |
| if (plane_.width == 0 || plane_.height == 0) { |
| return buf_ptrs; |
| } |
| |
| for (int i = 0; i < ROWS; i++) { |
| int y = i + y_base; |
| y = min(y, transform_.output_height() - 1); |
| |
| int output_width = padded_row_length_; |
| output_width = min(output_width, transform_.output_width()); |
| output_width = min(output_width, plane_.width); |
| |
| // Each row in the output image will be copied into buf_ by gathering pixels |
| // along an axis-aligned line in the plane. |
| // The line is defined by (startX, startY) -> (endX, endY), computed via the |
| // current Transform. |
| int startX; |
| int startY; |
| transform_.Map(0, y, &startX, &startY); |
| |
| int endX; |
| int endY; |
| transform_.Map(output_width - 1, y, &endX, &endY); |
| |
| // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane. |
| startX = min(startX, plane_.width - 1); |
| startY = min(startY, plane_.height - 1); |
| endX = min(endX, plane_.width - 1); |
| endY = min(endY, plane_.height - 1); |
| startX = max(startX, 0); |
| startY = max(startY, 0); |
| endX = max(endX, 0); |
| endY = max(endY, 0); |
| |
| // To reduce work inside the copy-loop, precompute the start, end, and |
| // stride relating the values to be gathered from plane_ into buf |
| // for this particular scan-line. |
| int dx = sgn(endX - startX); |
| int dy = sgn(endY - startY); |
| assert(dx == 0 || dy == 0); |
| // The index into plane_.data of (startX, startY) |
| int plane_start = startX * plane_.pixel_stride + startY * plane_.row_stride; |
| // The index into plane_.data of (endX, endY) |
| int plane_end = endX * plane_.pixel_stride + endY * plane_.row_stride; |
| // The stride, in terms of indices in plane_data, required to enumerate the |
| // samples between the start and end points. |
| int stride = dx * plane_.pixel_stride + dy * plane_.row_stride; |
| // In the degenerate-case of a 1x1 plane, startX and endX are equal, so |
| // stride would be 0, resulting in an infinite-loop. To avoid this case, |
| // use a stride of at-least 1. |
| if (stride == 0) { |
| stride = 1; |
| } |
| |
| int outX = 0; |
| for (int idx = plane_start; idx >= min(plane_start, plane_end) && |
| idx <= max(plane_start, plane_end); |
| idx += stride) { |
| buf_ptrs[i][outX] = plane_.data[idx]; |
| outX++; |
| } |
| |
| // Fill the remaining right-edge of the buffer by extending the last |
| // value. |
| unsigned char right_padding_value = buf_ptrs[i][outX - 1]; |
| // TODO OPTIMIZE Use memset instead. |
| for (; outX < padded_row_length_; outX++) { |
| buf_ptrs[i][outX] = right_padding_value; |
| } |
| } |
| |
| return buf_ptrs; |
| } |