xref: /aosp_15_r20/external/libultrahdr/fuzzer/ultrahdr_legacy_fuzzer.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1 /*
2  * Copyright 2023 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 
17 #include <fuzzer/FuzzedDataProvider.h>
18 #include <algorithm>
19 #include <iostream>
20 #include <memory>
21 #include <random>
22 
23 #include "ultrahdr/ultrahdrcommon.h"
24 #include "ultrahdr/gainmapmath.h"
25 #include "ultrahdr/jpegr.h"
26 
27 using namespace ultrahdr;
28 
29 // Color gamuts for image data, sync with ultrahdr.h
30 const int kCgMin = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
31 const int kCgMax = ULTRAHDR_COLORGAMUT_BT2100;
32 
33 // Transfer functions for image data, sync with ultrahdr.h
34 const int kTfMin = ULTRAHDR_TF_UNSPECIFIED;
35 const int kTfMax = ULTRAHDR_TF_SRGB;
36 
37 // Transfer functions for image data, sync with ultrahdr.h
38 const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED;
39 const int kOfMax = ULTRAHDR_OUTPUT_HDR_HLG;
40 
41 // quality factor
42 const int kQfMin = -10;
43 const int kQfMax = 110;
44 
45 class UltraHdrEncFuzzer {
46  public:
UltraHdrEncFuzzer(const uint8_t * data,size_t size)47   UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {};
48   void process();
49   template <typename T>
50   void fillBuffer(T* data, int width, int height, int stride);
51 
52  private:
53   FuzzedDataProvider mFdp;
54 };
55 
56 template <typename T>
fillBuffer(T * data,int width,int height,int stride)57 void UltraHdrEncFuzzer::fillBuffer(T* data, int width, int height, int stride) {
58   if (!mFdp.remaining_bytes()) return;
59 
60   T* tmp = data;
61   std::vector<T> buffer(width);
62   for (int i = 0; i < buffer.size(); i++) {
63     buffer[i] = mFdp.ConsumeIntegral<T>();
64   }
65   for (int j = 0; j < height; j++) {
66     for (int i = 0; i < width; i += buffer.size()) {
67       memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
68       std::shuffle(buffer.begin(), buffer.end(),
69                    std::default_random_engine(std::random_device{}()));
70     }
71     tmp += stride;
72   }
73 }
74 
process()75 void UltraHdrEncFuzzer::process() {
76   if (mFdp.remaining_bytes()) {
77     struct jpegr_uncompressed_struct p010Img{};
78     struct jpegr_uncompressed_struct yuv420Img{};
79     struct jpegr_uncompressed_struct grayImg{};
80     struct jpegr_compressed_struct jpegImgR{};
81     struct jpegr_compressed_struct jpegImg{};
82     struct jpegr_compressed_struct jpegGainMap{};
83 
84     // which encode api to select
85     int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
86 
87     // quality factor
88     int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
89 
90     // hdr_tf
91     auto tf =
92         static_cast<ultrahdr_transfer_function>(mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
93 
94     // p010 Cg
95     auto p010Cg =
96         static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
97 
98     // 420 Cg
99     auto yuv420Cg =
100         static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
101 
102     // hdr_of
103     auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
104 
105     int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
106     width = (width >> 1) << 1;
107 
108     int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
109     height = (height >> 1) << 1;
110 
111     // gain_map quality factor
112     auto gainmap_quality = mFdp.ConsumeIntegral<int8_t>();
113 
114     // multi channel gainmap
115     auto multi_channel_gainmap = mFdp.ConsumeIntegral<int8_t>();
116 
117     // gainmap scale factor
118     auto gm_scale_factor = mFdp.ConsumeIntegralInRange<int16_t>(-32, 192);
119 
120     // encoding speed preset
121     auto enc_preset = mFdp.ConsumeBool() ? UHDR_USAGE_REALTIME : UHDR_USAGE_BEST_QUALITY;
122 
123     // gainmap metadata
124     auto minBoost = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 64.0f);
125     auto maxBoost = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 64.0f);
126     auto gamma = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 5);
127     auto offsetSdr = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 1.0f);
128     auto offsetHdr = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 1.0f);
129     auto minCapacity = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 48.0f);
130     auto maxCapacity = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 48.0f);
131 
132     // target display peak brightness
133     auto targetDispPeakBrightness = mFdp.ConsumeFloatingPointInRange<float>(100.0f, 10500.0f);
134 
135     // raw buffer config
136     bool hasP010Stride = mFdp.ConsumeBool();
137     size_t yP010Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
138     if (!hasP010Stride) yP010Stride = width;
139     bool isP010UVContiguous = mFdp.ConsumeBool();
140     bool hasP010UVStride = mFdp.ConsumeBool();
141     size_t uvP010Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
142     if (!hasP010UVStride) uvP010Stride = width;
143 
144     bool hasYuv420Stride = mFdp.ConsumeBool();
145     size_t yYuv420Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
146     if (!hasYuv420Stride) yYuv420Stride = width;
147     bool isYuv420UVContiguous = mFdp.ConsumeBool();
148     bool hasYuv420UVStride = mFdp.ConsumeBool();
149     size_t uvYuv420Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width / 2, width / 2 + 128);
150     if (!hasYuv420UVStride) uvYuv420Stride = width / 2;
151 
152     // display boost
153     float displayBoost = mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX);
154 
155     std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
156     std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
157     std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
158     std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
159     std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
160     if (muxSwitch != 4) {
161       // init p010 image
162       p010Img.width = width;
163       p010Img.height = height;
164       p010Img.colorGamut = p010Cg;
165       p010Img.luma_stride = yP010Stride;
166       if (isP010UVContiguous) {
167         size_t p010Size = yP010Stride * height * 3 / 2;
168         bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
169         p010Img.data = bufferYHdr.get();
170         p010Img.chroma_data = nullptr;
171         p010Img.chroma_stride = 0;
172         fillBuffer<uint16_t>(bufferYHdr.get(), width, height, yP010Stride);
173         fillBuffer<uint16_t>(bufferYHdr.get() + yP010Stride * height, width, height / 2,
174                              yP010Stride);
175       } else {
176         size_t p010YSize = yP010Stride * height;
177         bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
178         p010Img.data = bufferYHdr.get();
179         fillBuffer<uint16_t>(bufferYHdr.get(), width, height, yP010Stride);
180         size_t p010UVSize = uvP010Stride * p010Img.height / 2;
181         bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
182         p010Img.chroma_data = bufferUVHdr.get();
183         p010Img.chroma_stride = uvP010Stride;
184         fillBuffer<uint16_t>(bufferUVHdr.get(), width, height / 2, uvP010Stride);
185       }
186     } else {
187       size_t map_width = width / kMapDimensionScaleFactorDefault;
188       size_t map_height = height / kMapDimensionScaleFactorDefault;
189       // init 400 image
190       grayImg.width = map_width;
191       grayImg.height = map_height;
192       grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
193       const size_t graySize = map_width * map_height;
194       grayImgRaw = std::make_unique<uint8_t[]>(graySize);
195       grayImg.data = grayImgRaw.get();
196       fillBuffer<uint8_t>(grayImgRaw.get(), map_width, map_height, map_width);
197       grayImg.chroma_data = nullptr;
198       grayImg.luma_stride = 0;
199       grayImg.chroma_stride = 0;
200     }
201 
202     if (muxSwitch > 0) {
203       // init 420 image
204       yuv420Img.width = width;
205       yuv420Img.height = height;
206       yuv420Img.colorGamut = yuv420Cg;
207       yuv420Img.luma_stride = yYuv420Stride;
208       if (isYuv420UVContiguous) {
209         size_t yuv420Size = yYuv420Stride * height * 3 / 2;
210         bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
211         yuv420Img.data = bufferYSdr.get();
212         yuv420Img.chroma_data = nullptr;
213         yuv420Img.chroma_stride = 0;
214         fillBuffer<uint8_t>(bufferYSdr.get(), width, height, yYuv420Stride);
215         fillBuffer<uint8_t>(bufferYSdr.get() + yYuv420Stride * height, width / 2, height / 2,
216                             yYuv420Stride / 2);
217         fillBuffer<uint8_t>(bufferYSdr.get() + yYuv420Stride * height * 5 / 4, width / 2,
218                             height / 2, yYuv420Stride / 2);
219       } else {
220         size_t yuv420YSize = yYuv420Stride * height;
221         bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
222         yuv420Img.data = bufferYSdr.get();
223         fillBuffer<uint8_t>(bufferYSdr.get(), width, height, yYuv420Stride);
224         size_t yuv420UVSize = uvYuv420Stride * yuv420Img.height / 2 * 2;
225         bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
226         yuv420Img.chroma_data = bufferUVSdr.get();
227         yuv420Img.chroma_stride = uvYuv420Stride;
228         fillBuffer<uint8_t>(bufferUVSdr.get(), width / 2, height / 2, uvYuv420Stride);
229         fillBuffer<uint8_t>(bufferUVSdr.get() + uvYuv420Stride * height / 2, width / 2, height / 2,
230                             uvYuv420Stride);
231       }
232     }
233 
234     // dest
235     // 2 * p010 size as input data is random, DCT compression might not behave as expected
236     jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
237     auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
238     jpegImgR.data = jpegImgRaw.get();
239 // #define DUMP_PARAM
240 #ifdef DUMP_PARAM
241     std::cout << "Api Select " << muxSwitch << std::endl;
242     std::cout << "image dimensions " << width << " x " << height << std::endl;
243     std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
244     std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
245     std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
246     std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
247     std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
248     std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
249     std::cout << "quality factor " << quality << std::endl;
250 #endif
251     JpegR jpegHdr(nullptr, gm_scale_factor, gainmap_quality, multi_channel_gainmap, gamma,
252                   enc_preset, minBoost, maxBoost, targetDispPeakBrightness);
253     status_t status = JPEGR_UNKNOWN_ERROR;
254     if (muxSwitch == 0) {  // api 0
255       jpegImgR.length = 0;
256       status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
257     } else if (muxSwitch == 1) {  // api 1
258       jpegImgR.length = 0;
259       status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
260     } else {
261       // compressed img
262       JpegEncoderHelper encoder;
263       struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
264       if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
265       if (!yuv420ImgCopy.chroma_data) {
266         uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
267         yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
268         yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
269       }
270       const uint8_t* planes[3]{reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
271                                reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
272                                reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data) +
273                                    yuv420ImgCopy.chroma_stride * yuv420ImgCopy.height / 2};
274       const unsigned int strides[3]{yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
275                                     yuv420ImgCopy.chroma_stride};
276       if (encoder
277               .compressImage(planes, strides, yuv420ImgCopy.width, yuv420ImgCopy.height,
278                              UHDR_IMG_FMT_12bppYCbCr420, quality, nullptr, 0)
279               .error_code == UHDR_CODEC_OK) {
280         jpegImg.length = encoder.getCompressedImageSize();
281         jpegImg.maxLength = jpegImg.length;
282         jpegImg.data = encoder.getCompressedImagePtr();
283         jpegImg.colorGamut = yuv420Cg;
284         if (muxSwitch == 2) {  // api 2
285           jpegImgR.length = 0;
286           status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
287         } else if (muxSwitch == 3) {  // api 3
288           jpegImgR.length = 0;
289           status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
290         } else if (muxSwitch == 4) {  // api 4
291           jpegImgR.length = 0;
292           JpegEncoderHelper gainMapEncoder;
293           const uint8_t* planeGm[1]{reinterpret_cast<uint8_t*>(grayImg.data)};
294           const unsigned int strideGm[1]{grayImg.width};
295           if (gainMapEncoder
296                   .compressImage(planeGm, strideGm, grayImg.width, grayImg.height,
297                                  UHDR_IMG_FMT_8bppYCbCr400, quality, nullptr, 0)
298                   .error_code == UHDR_CODEC_OK) {
299             jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
300             jpegGainMap.maxLength = jpegImg.length;
301             jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
302             jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
303             ultrahdr_metadata_struct metadata;
304             metadata.version = kJpegrVersion;
305             metadata.maxContentBoost = maxBoost;
306             metadata.minContentBoost = minBoost;
307             metadata.gamma = gamma;
308             metadata.offsetSdr = offsetSdr;
309             metadata.offsetHdr = offsetHdr;
310             metadata.hdrCapacityMin = minCapacity;
311             metadata.hdrCapacityMax = maxCapacity;
312             status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
313           }
314         }
315       }
316     }
317     if (status == JPEGR_NO_ERROR) {
318       jpegr_info_struct info{};
319       status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
320       if (status == JPEGR_NO_ERROR) {
321         size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
322         jpegr_uncompressed_struct decodedJpegR;
323         auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
324         decodedJpegR.data = decodedRaw.get();
325         ultrahdr_metadata_struct metadata;
326         status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR, displayBoost, nullptr, of, nullptr,
327                                      &metadata);
328         if (status != JPEGR_NO_ERROR) {
329           ALOGE("encountered error during decoding %d", status);
330         }
331       } else {
332         ALOGE("encountered error during get jpeg info %d", status);
333       }
334     } else {
335       ALOGE("encountered error during encoding %d", status);
336     }
337   }
338 }
339 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)340 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
341   UltraHdrEncFuzzer fuzzHandle(data, size);
342   fuzzHandle.process();
343   return 0;
344 }
345