Home | History | Annotate | Download | only in jni
      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 
     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 (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