Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eca8194f authored by Ram Mohan's avatar Ram Mohan Committed by Dichen Zhang
Browse files

ultrahdr: update jpegencoderhelper to accept uncompressed struct fields

With this change we can now pass luma/chroma ptrs and stride information
to jpegencoderhelper class during compression. This by passes
intermediate copy whenever possible

Also updated fuzzer to incorporate 420 stride support

updated jpegr unit tests for more combinations of gamuts and unusual strides

Bug: 294218453
Test: ./ultrahdr_unit_test
Test: ./ultrahdr_enc_fuzzer

Change-Id: Ic50dd34b0c680618e73e0cb27f554b9bf8272e8f
parent 88c57a25
Loading
Loading
Loading
Loading
+78 −38
Original line number Original line Diff line number Diff line
@@ -23,8 +23,8 @@


// User include files
// User include files
#include "ultrahdr/gainmapmath.h"
#include "ultrahdr/gainmapmath.h"
#include "ultrahdr/jpegencoderhelper.h"
#include "ultrahdr/jpegdecoderhelper.h"
#include "ultrahdr/jpegdecoderhelper.h"
#include "ultrahdr/jpegencoderhelper.h"
#include "utils/Log.h"
#include "utils/Log.h"


using namespace android::ultrahdr;
using namespace android::ultrahdr;
@@ -50,7 +50,7 @@ public:
    UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
    UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
    void process();
    void process();
    void fillP010Buffer(uint16_t* data, int width, int height, int stride);
    void fillP010Buffer(uint16_t* data, int width, int height, int stride);
    void fill420Buffer(uint8_t* data, int size);
    void fill420Buffer(uint8_t* data, int width, int height, int stride);


private:
private:
    FuzzedDataProvider mFdp;
    FuzzedDataProvider mFdp;
@@ -73,14 +73,19 @@ void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, in
    }
    }
}
}


void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int size) {
void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) {
    uint8_t* tmp = data;
    std::vector<uint8_t> buffer(16);
    std::vector<uint8_t> buffer(16);
    mFdp.ConsumeData(buffer.data(), buffer.size());
    mFdp.ConsumeData(buffer.data(), buffer.size());
    for (int i = 0; i < size; i += buffer.size()) {
    for (int j = 0; j < height; j++) {
        memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (size - i)));
        for (int i = 0; i < width; i += buffer.size()) {
            memcpy(tmp + i, buffer.data(),
                   std::min((int)buffer.size(), (width - i)) * sizeof(*data));
            std::shuffle(buffer.begin(), buffer.end(),
            std::shuffle(buffer.begin(), buffer.end(),
                         std::default_random_engine(std::random_device{}()));
                         std::default_random_engine(std::random_device{}()));
        }
        }
        tmp += stride;
    }
}
}


void UltraHdrEncFuzzer::process() {
void UltraHdrEncFuzzer::process() {
@@ -120,9 +125,10 @@ void UltraHdrEncFuzzer::process() {
        int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
        int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
        height = (height >> 1) << 1;
        height = (height >> 1) << 1;


        std::unique_ptr<uint16_t[]> bufferY = nullptr;
        std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
        std::unique_ptr<uint16_t[]> bufferUV = nullptr;
        std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
        std::unique_ptr<uint8_t[]> yuv420ImgRaw = nullptr;
        std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
        std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
        std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
        std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
        if (muxSwitch != 4) {
        if (muxSwitch != 4) {
            // init p010 image
            // init p010 image
@@ -136,30 +142,29 @@ void UltraHdrEncFuzzer::process() {
            int bppP010 = 2;
            int bppP010 = 2;
            if (isUVContiguous) {
            if (isUVContiguous) {
                size_t p010Size = yStride * height * 3 / 2;
                size_t p010Size = yStride * height * 3 / 2;
                bufferY = std::make_unique<uint16_t[]>(p010Size);
                bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
                p010Img.data = bufferY.get();
                p010Img.data = bufferYHdr.get();
                p010Img.chroma_data = nullptr;
                p010Img.chroma_data = nullptr;
                p010Img.chroma_stride = 0;
                p010Img.chroma_stride = 0;
                fillP010Buffer(bufferY.get(), width, height, yStride);
                fillP010Buffer(bufferYHdr.get(), width, height, yStride);
                fillP010Buffer(bufferY.get() + yStride * height, width, height / 2, yStride);
                fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
            } else {
            } else {
                int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
                int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
                size_t p010YSize = yStride * height;
                size_t p010YSize = yStride * height;
                bufferY = std::make_unique<uint16_t[]>(p010YSize);
                bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
                p010Img.data = bufferY.get();
                p010Img.data = bufferYHdr.get();
                fillP010Buffer(bufferY.get(), width, height, yStride);
                fillP010Buffer(bufferYHdr.get(), width, height, yStride);
                size_t p010UVSize = uvStride * p010Img.height / 2;
                size_t p010UVSize = uvStride * p010Img.height / 2;
                bufferUV = std::make_unique<uint16_t[]>(p010UVSize);
                bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
                p010Img.chroma_data = bufferUV.get();
                p010Img.chroma_data = bufferUVHdr.get();
                p010Img.chroma_stride = uvStride;
                p010Img.chroma_stride = uvStride;
                fillP010Buffer(bufferUV.get(), width, height / 2, uvStride);
                fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
            }
            }
        } else {
        } else {
            int map_width = width / kMapDimensionScaleFactor;
            size_t map_width = static_cast<size_t>(
            int map_height = height / kMapDimensionScaleFactor;
                    floor((width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
            map_width = static_cast<size_t>(floor((map_width + kJpegBlock - 1) / kJpegBlock)) *
            size_t map_height = static_cast<size_t>(
                    kJpegBlock;
                    floor((height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
            map_height = ((map_height + 1) >> 1) << 1;
            // init 400 image
            // init 400 image
            grayImg.width = map_width;
            grayImg.width = map_width;
            grayImg.height = map_height;
            grayImg.height = map_height;
@@ -168,7 +173,7 @@ void UltraHdrEncFuzzer::process() {
            const size_t graySize = map_width * map_height;
            const size_t graySize = map_width * map_height;
            grayImgRaw = std::make_unique<uint8_t[]>(graySize);
            grayImgRaw = std::make_unique<uint8_t[]>(graySize);
            grayImg.data = grayImgRaw.get();
            grayImg.data = grayImgRaw.get();
            fill420Buffer(grayImgRaw.get(), graySize);
            fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
            grayImg.chroma_data = nullptr;
            grayImg.chroma_data = nullptr;
            grayImg.luma_stride = 0;
            grayImg.luma_stride = 0;
            grayImg.chroma_stride = 0;
            grayImg.chroma_stride = 0;
@@ -176,17 +181,38 @@ void UltraHdrEncFuzzer::process() {


        if (muxSwitch > 0) {
        if (muxSwitch > 0) {
            // init 420 image
            // init 420 image
            bool isUVContiguous = mFdp.ConsumeBool();
            bool hasYStride = mFdp.ConsumeBool();
            int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
            yuv420Img.width = width;
            yuv420Img.width = width;
            yuv420Img.height = height;
            yuv420Img.height = height;
            yuv420Img.colorGamut = yuv420Cg;
            yuv420Img.colorGamut = yuv420Cg;

            yuv420Img.luma_stride = hasYStride ? yStride : 0;
            const size_t yuv420Size = (yuv420Img.width * yuv420Img.height * 3) / 2;
            if (isUVContiguous) {
            yuv420ImgRaw = std::make_unique<uint8_t[]>(yuv420Size);
                size_t yuv420Size = yStride * height * 3 / 2;
            yuv420Img.data = yuv420ImgRaw.get();
                bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
            fill420Buffer(yuv420ImgRaw.get(), yuv420Size);
                yuv420Img.data = bufferYSdr.get();
                yuv420Img.chroma_data = nullptr;
                yuv420Img.chroma_data = nullptr;
            yuv420Img.luma_stride = 0;
                yuv420Img.chroma_stride = 0;
                yuv420Img.chroma_stride = 0;
                fill420Buffer(bufferYSdr.get(), width, height, yStride);
                fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2,
                              yStride / 2);
                fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
                              yStride / 2);
            } else {
                int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
                size_t yuv420YSize = yStride * height;
                bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
                yuv420Img.data = bufferYSdr.get();
                fill420Buffer(bufferYSdr.get(), width, height, yStride);
                size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
                bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
                yuv420Img.chroma_data = bufferYSdr.get();
                yuv420Img.chroma_stride = uvStride;
                fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
                fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2,
                              uvStride);
            }
        }
        }


        // dest
        // dest
@@ -203,6 +229,8 @@ void UltraHdrEncFuzzer::process() {
        std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
        std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
        std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
        std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
        std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
        std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
        std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
        std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
        std::cout << "quality factor " << quality << std::endl;
        std::cout << "quality factor " << quality << std::endl;
#endif
#endif


@@ -217,8 +245,19 @@ void UltraHdrEncFuzzer::process() {
        } else {
        } else {
            // compressed img
            // compressed img
            JpegEncoderHelper encoder;
            JpegEncoderHelper encoder;
            if (encoder.compressImage(yuv420Img.data, yuv420Img.width, yuv420Img.height, quality,
            struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
                                      nullptr, 0)) {
            if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
            if (!yuv420ImgCopy.chroma_data) {
                uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
                yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
                yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
            }

            if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
                                      reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
                                      yuv420ImgCopy.width, yuv420ImgCopy.height,
                                      yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
                                      quality, nullptr, 0)) {
                jpegImg.length = encoder.getCompressedImageSize();
                jpegImg.length = encoder.getCompressedImageSize();
                jpegImg.maxLength = jpegImg.length;
                jpegImg.maxLength = jpegImg.length;
                jpegImg.data = encoder.getCompressedImagePtr();
                jpegImg.data = encoder.getCompressedImagePtr();
@@ -233,8 +272,9 @@ void UltraHdrEncFuzzer::process() {
                } else if (muxSwitch == 4) { // api 4
                } else if (muxSwitch == 4) { // api 4
                    jpegImgR.length = 0;
                    jpegImgR.length = 0;
                    JpegEncoderHelper gainMapEncoder;
                    JpegEncoderHelper gainMapEncoder;
                    if (gainMapEncoder.compressImage(grayImg.data, grayImg.width, grayImg.height,
                    if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data),
                                                     quality, nullptr, 0, true)) {
                                                     nullptr, grayImg.width, grayImg.height,
                                                     grayImg.width, 0, quality, nullptr, 0)) {
                        jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
                        jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
                        jpegGainMap.maxLength = jpegImg.length;
                        jpegGainMap.maxLength = jpegImg.length;
                        jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
                        jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
+13 −8
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@


// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
#include <cstdio>
#include <cstdio>
#include <vector>


extern "C" {
extern "C" {
#include <jerror.h>
#include <jerror.h>
@@ -26,10 +27,11 @@ extern "C" {
}
}


#include <utils/Errors.h>
#include <utils/Errors.h>
#include <vector>


namespace android::ultrahdr {
namespace android::ultrahdr {


#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))

/*
/*
 * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
 * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
 * This class is not thread-safe.
 * This class is not thread-safe.
@@ -46,8 +48,9 @@ public:
     * ICC segment which will be added to the compressed image.
     * ICC segment which will be added to the compressed image.
     * Returns false if errors occur during compression.
     * Returns false if errors occur during compression.
     */
     */
    bool compressImage(const void* image, int width, int height, int quality,
    bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
                       const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false);
                       int lumaStride, int chromaStride, int quality, const void* iccBuffer,
                       unsigned int iccSize);


    /*
    /*
     * Returns the compressed JPEG buffer pointer. This method must be called only after calling
     * Returns the compressed JPEG buffer pointer. This method must be called only after calling
@@ -66,6 +69,7 @@ public:
     * We must pass at least 16 scanlines according to libjpeg documentation.
     * We must pass at least 16 scanlines according to libjpeg documentation.
     */
     */
    static const int kCompressBatchSize = 16;
    static const int kCompressBatchSize = 16;

private:
private:
    // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
    // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
    // passed into jpeg library.
    // passed into jpeg library.
@@ -75,15 +79,16 @@ private:
    static void outputErrorMessage(j_common_ptr cinfo);
    static void outputErrorMessage(j_common_ptr cinfo);


    // Returns false if errors occur.
    // Returns false if errors occur.
    bool encode(const void* inYuv, int width, int height, int jpegQuality,
    bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
                const void* iccBuffer, unsigned int iccSize, bool isSingleChannel);
                int lumaStride, int chromaStride, int quality, const void* iccBuffer,
                unsigned int iccSize);
    void setJpegDestination(jpeg_compress_struct* cinfo);
    void setJpegDestination(jpeg_compress_struct* cinfo);
    void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
    void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
                               bool isSingleChannel);
                               bool isSingleChannel);
    // Returns false if errors occur.
    // Returns false if errors occur.
    bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel);
    bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer,
    bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv);
                     int lumaStride, int chromaStride);
    bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image);
    bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride);


    // The block size for encoded jpeg image buffer.
    // The block size for encoded jpeg image buffer.
    static const int kBlockSize = 16384;
    static const int kBlockSize = 16384;
+34 −39
Original line number Original line Diff line number Diff line
@@ -23,8 +23,6 @@


namespace android::ultrahdr {
namespace android::ultrahdr {


#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))

// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
struct destination_mgr {
struct destination_mgr {
    struct jpeg_destination_mgr mgr;
    struct jpeg_destination_mgr mgr;
@@ -35,11 +33,12 @@ JpegEncoderHelper::JpegEncoderHelper() {}


JpegEncoderHelper::~JpegEncoderHelper() {}
JpegEncoderHelper::~JpegEncoderHelper() {}


bool JpegEncoderHelper::compressImage(const void* image, int width, int height, int quality,
bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
                                      const void* iccBuffer, unsigned int iccSize,
                                      int height, int lumaStride, int chromaStride, int quality,
                                      bool isSingleChannel) {
                                      const void* iccBuffer, unsigned int iccSize) {
    mResultBuffer.clear();
    mResultBuffer.clear();
    if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) {
    if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer,
                iccSize)) {
        return false;
        return false;
    }
    }
    ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
    ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
@@ -87,25 +86,24 @@ void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) {
    ALOGE("%s\n", buffer);
    ALOGE("%s\n", buffer);
}
}


bool JpegEncoderHelper::encode(const void* image, int width, int height, int jpegQuality,
bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
                               const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) {
                               int height, int lumaStride, int chromaStride, int quality,
                               const void* iccBuffer, unsigned int iccSize) {
    jpeg_compress_struct cinfo;
    jpeg_compress_struct cinfo;
    jpeg_error_mgr jerr;
    jpeg_error_mgr jerr;


    cinfo.err = jpeg_std_error(&jerr);
    cinfo.err = jpeg_std_error(&jerr);
    // Override output_message() to print error log with ALOGE().
    cinfo.err->output_message = &outputErrorMessage;
    cinfo.err->output_message = &outputErrorMessage;
    jpeg_create_compress(&cinfo);
    jpeg_create_compress(&cinfo);
    setJpegDestination(&cinfo);
    setJpegDestination(&cinfo);

    setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr);
    setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel);
    jpeg_start_compress(&cinfo, TRUE);
    jpeg_start_compress(&cinfo, TRUE);

    if (iccBuffer != nullptr && iccSize > 0) {
    if (iccBuffer != nullptr && iccSize > 0) {
        jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
        jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
    }
    }

    bool status = cinfo.num_components == 1
    bool status = compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel);
            ? compressY(&cinfo, yBuffer, lumaStride)
            : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride);
    jpeg_finish_compress(&cinfo);
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);


@@ -141,27 +139,23 @@ void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality
    }
    }
}
}


bool JpegEncoderHelper::compress(jpeg_compress_struct* cinfo, const uint8_t* image,
bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
                                 bool isSingleChannel) {
                                    const uint8_t* uvBuffer, int lumaStride, int chromaStride) {
    return isSingleChannel ? compressSingleChannel(cinfo, image) : compressYuv(cinfo, image);
}

bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) {
    JSAMPROW y[kCompressBatchSize];
    JSAMPROW y[kCompressBatchSize];
    JSAMPROW cb[kCompressBatchSize / 2];
    JSAMPROW cb[kCompressBatchSize / 2];
    JSAMPROW cr[kCompressBatchSize / 2];
    JSAMPROW cr[kCompressBatchSize / 2];
    JSAMPARRAY planes[3]{y, cb, cr};
    JSAMPARRAY planes[3]{y, cb, cr};


    size_t y_plane_size = cinfo->image_width * cinfo->image_height;
    size_t y_plane_size = lumaStride * cinfo->image_height;
    size_t uv_plane_size = y_plane_size / 4;
    size_t u_plane_size = chromaStride * cinfo->image_height / 2;
    uint8_t* y_plane = const_cast<uint8_t*>(yuv);
    uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
    uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
    uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer);
    uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
    uint8_t* v_plane = const_cast<uint8_t*>(u_plane + u_plane_size);
    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
    memset(empty.get(), 0, cinfo->image_width);
    memset(empty.get(), 0, cinfo->image_width);


    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
    const bool is_width_aligned = (aligned_width == cinfo->image_width);
    const bool need_padding = (lumaStride < aligned_width);
    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
    uint8_t* y_plane_intrm = nullptr;
    uint8_t* y_plane_intrm = nullptr;
    uint8_t* u_plane_intrm = nullptr;
    uint8_t* u_plane_intrm = nullptr;
@@ -170,7 +164,7 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
    JSAMPROW cb_intrm[kCompressBatchSize / 2];
    JSAMPROW cb_intrm[kCompressBatchSize / 2];
    JSAMPROW cr_intrm[kCompressBatchSize / 2];
    JSAMPROW cr_intrm[kCompressBatchSize / 2];
    JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
    JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
    if (!is_width_aligned) {
    if (need_padding) {
        size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
        size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
        y_plane_intrm = buffer_intrm.get();
        y_plane_intrm = buffer_intrm.get();
@@ -195,11 +189,11 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
        for (int i = 0; i < kCompressBatchSize; ++i) {
        for (int i = 0; i < kCompressBatchSize; ++i) {
            size_t scanline = cinfo->next_scanline + i;
            size_t scanline = cinfo->next_scanline + i;
            if (scanline < cinfo->image_height) {
            if (scanline < cinfo->image_height) {
                y[i] = y_plane + scanline * cinfo->image_width;
                y[i] = y_plane + scanline * lumaStride;
            } else {
            } else {
                y[i] = empty.get();
                y[i] = empty.get();
            }
            }
            if (!is_width_aligned) {
            if (need_padding) {
                memcpy(y_intrm[i], y[i], cinfo->image_width);
                memcpy(y_intrm[i], y[i], cinfo->image_width);
            }
            }
        }
        }
@@ -207,18 +201,18 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
        for (int i = 0; i < kCompressBatchSize / 2; ++i) {
        for (int i = 0; i < kCompressBatchSize / 2; ++i) {
            size_t scanline = cinfo->next_scanline / 2 + i;
            size_t scanline = cinfo->next_scanline / 2 + i;
            if (scanline < cinfo->image_height / 2) {
            if (scanline < cinfo->image_height / 2) {
                int offset = scanline * (cinfo->image_width / 2);
                int offset = scanline * chromaStride;
                cb[i] = u_plane + offset;
                cb[i] = u_plane + offset;
                cr[i] = v_plane + offset;
                cr[i] = v_plane + offset;
            } else {
            } else {
                cb[i] = cr[i] = empty.get();
                cb[i] = cr[i] = empty.get();
            }
            }
            if (!is_width_aligned) {
            if (need_padding) {
                memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
                memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
                memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
                memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
            }
            }
        }
        }
        int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
        int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
                                            kCompressBatchSize);
                                            kCompressBatchSize);
        if (processed != kCompressBatchSize) {
        if (processed != kCompressBatchSize) {
            ALOGE("Number of processed lines does not equal input lines.");
            ALOGE("Number of processed lines does not equal input lines.");
@@ -228,22 +222,23 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t*
    return true;
    return true;
}
}


bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) {
bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
                                  int lumaStride) {
    JSAMPROW y[kCompressBatchSize];
    JSAMPROW y[kCompressBatchSize];
    JSAMPARRAY planes[1]{y};
    JSAMPARRAY planes[1]{y};


    uint8_t* y_plane = const_cast<uint8_t*>(image);
    uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
    std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
    memset(empty.get(), 0, cinfo->image_width);
    memset(empty.get(), 0, cinfo->image_width);


    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
    const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
    bool is_width_aligned = (aligned_width == cinfo->image_width);
    const bool need_padding = (lumaStride < aligned_width);
    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
    std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
    uint8_t* y_plane_intrm = nullptr;
    uint8_t* y_plane_intrm = nullptr;
    uint8_t* u_plane_intrm = nullptr;
    uint8_t* u_plane_intrm = nullptr;
    JSAMPROW y_intrm[kCompressBatchSize];
    JSAMPROW y_intrm[kCompressBatchSize];
    JSAMPARRAY planes_intrm[]{y_intrm};
    JSAMPARRAY planes_intrm[]{y_intrm};
    if (!is_width_aligned) {
    if (need_padding) {
        size_t mcu_row_size = aligned_width * kCompressBatchSize;
        size_t mcu_row_size = aligned_width * kCompressBatchSize;
        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
        buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
        y_plane_intrm = buffer_intrm.get();
        y_plane_intrm = buffer_intrm.get();
@@ -257,15 +252,15 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const
        for (int i = 0; i < kCompressBatchSize; ++i) {
        for (int i = 0; i < kCompressBatchSize; ++i) {
            size_t scanline = cinfo->next_scanline + i;
            size_t scanline = cinfo->next_scanline + i;
            if (scanline < cinfo->image_height) {
            if (scanline < cinfo->image_height) {
                y[i] = y_plane + scanline * cinfo->image_width;
                y[i] = y_plane + scanline * lumaStride;
            } else {
            } else {
                y[i] = empty.get();
                y[i] = empty.get();
            }
            }
            if (!is_width_aligned) {
            if (need_padding) {
                memcpy(y_intrm[i], y[i], cinfo->image_width);
                memcpy(y_intrm[i], y[i], cinfo->image_width);
            }
            }
        }
        }
        int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
        int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
                                            kCompressBatchSize);
                                            kCompressBatchSize);
        if (processed != kCompressBatchSize / 2) {
        if (processed != kCompressBatchSize / 2) {
            ALOGE("Number of processed lines does not equal input lines.");
            ALOGE("Number of processed lines does not equal input lines.");
+69 −40

File changed.

Preview size limit exceeded, changes collapsed.

+10 −7
Original line number Original line Diff line number Diff line
@@ -120,7 +120,7 @@ public:
      0xB0, 0xB1,
      0xB0, 0xB1,
      0xB2, 0xB3,
      0xB2, 0xB3,
    };
    };
    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2 };
  }
  }


  Color (*Yuv420Colors())[4] {
  Color (*Yuv420Colors())[4] {
@@ -153,7 +153,7 @@ public:
      0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
      0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
      0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
      0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
    };
    };
    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 };
    return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4 };
  }
  }


  Color (*P010Colors())[4] {
  Color (*P010Colors())[4] {
@@ -625,7 +625,7 @@ TEST_F(GainMapMathTest, Bt2100ToBt601YuvConversion) {
  EXPECT_YUV_NEAR(yuv2100To601(yuv_b), P3YuvBlue());
  EXPECT_YUV_NEAR(yuv2100To601(yuv_b), P3YuvBlue());
}
}


TEST_F(GainMapMathTest, DISABLED_TransformYuv420) {
TEST_F(GainMapMathTest, TransformYuv420) {
  ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100,
  ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100,
                                    yuv2100To709, yuv2100To601 };
                                    yuv2100To709, yuv2100To601 };
  for (const ColorTransformFn& transform : transforms) {
  for (const ColorTransformFn& transform : transforms) {
@@ -636,6 +636,9 @@ TEST_F(GainMapMathTest, DISABLED_TransformYuv420) {
    memcpy(out_buf.get(), input.data, out_buf_size);
    memcpy(out_buf.get(), input.data, out_buf_size);
    jpegr_uncompressed_struct output = Yuv420Image();
    jpegr_uncompressed_struct output = Yuv420Image();
    output.data = out_buf.get();
    output.data = out_buf.get();
    output.chroma_data = out_buf.get() + input.width * input.height;
    output.luma_stride = input.width;
    output.chroma_stride = input.width / 2;


    transformYuv420(&output, 1, 1, transform);
    transformYuv420(&output, 1, 1, transform);


@@ -1042,7 +1045,7 @@ TEST_F(GainMapMathTest, ApplyGain) {
                applyGain(e, 1.0f, &metadata, displayBoost));
                applyGain(e, 1.0f, &metadata, displayBoost));
}
}


TEST_F(GainMapMathTest, DISABLED_GetYuv420Pixel) {
TEST_F(GainMapMathTest, GetYuv420Pixel) {
  jpegr_uncompressed_struct image = Yuv420Image();
  jpegr_uncompressed_struct image = Yuv420Image();
  Color (*colors)[4] = Yuv420Colors();
  Color (*colors)[4] = Yuv420Colors();


@@ -1053,7 +1056,7 @@ TEST_F(GainMapMathTest, DISABLED_GetYuv420Pixel) {
  }
  }
}
}


TEST_F(GainMapMathTest, DISABLED_GetP010Pixel) {
TEST_F(GainMapMathTest, GetP010Pixel) {
  jpegr_uncompressed_struct image = P010Image();
  jpegr_uncompressed_struct image = P010Image();
  Color (*colors)[4] = P010Colors();
  Color (*colors)[4] = P010Colors();


@@ -1064,7 +1067,7 @@ TEST_F(GainMapMathTest, DISABLED_GetP010Pixel) {
  }
  }
}
}


TEST_F(GainMapMathTest, DISABLED_SampleYuv420) {
TEST_F(GainMapMathTest, SampleYuv420) {
  jpegr_uncompressed_struct image = Yuv420Image();
  jpegr_uncompressed_struct image = Yuv420Image();
  Color (*colors)[4] = Yuv420Colors();
  Color (*colors)[4] = Yuv420Colors();


@@ -1090,7 +1093,7 @@ TEST_F(GainMapMathTest, DISABLED_SampleYuv420) {
  }
  }
}
}


TEST_F(GainMapMathTest, DISABLED_SampleP010) {
TEST_F(GainMapMathTest, SampleP010) {
  jpegr_uncompressed_struct image = P010Image();
  jpegr_uncompressed_struct image = P010Image();
  Color (*colors)[4] = P010Colors();
  Color (*colors)[4] = P010Colors();


Loading