blob: 5c8dc8747aa977ee62c3c7bf58339609c2c0d82a [file] [log] [blame]
msarett74114382015-03-16 11:55:18 -07001/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkCodecPriv_DEFINED
9#define SkCodecPriv_DEFINED
10
msarette6dd0042015-10-09 11:07:34 -070011#include "SkColorPriv.h"
msarettf7eb6fc2016-09-13 09:04:11 -070012#include "SkColorSpaceXform.h"
Matt Sarett909d3792016-12-22 10:52:25 -050013#include "SkColorSpaceXformPriv.h"
msarett9e43cab2015-04-29 07:38:43 -070014#include "SkColorTable.h"
Matt Sarett1a85ca52016-11-04 11:52:48 -040015#include "SkEncodedInfo.h"
msarett74114382015-03-16 11:55:18 -070016#include "SkImageInfo.h"
msarett74114382015-03-16 11:55:18 -070017#include "SkTypes.h"
18
scroggoc5560be2016-02-03 09:42:42 -080019#ifdef SK_PRINT_CODEC_MESSAGES
20 #define SkCodecPrintf SkDebugf
21#else
22 #define SkCodecPrintf(...)
23#endif
24
msarett3d9d7a72015-10-21 10:27:10 -070025// FIXME: Consider sharing with dm, nanbench, and tools.
msarettd1ec89b2016-08-03 12:59:27 -070026static inline float get_scale_from_sample_size(int sampleSize) {
msarett3d9d7a72015-10-21 10:27:10 -070027 return 1.0f / ((float) sampleSize);
28}
29
msarettd1ec89b2016-08-03 12:59:27 -070030static inline bool is_valid_subset(const SkIRect& subset, const SkISize& imageDims) {
msarett3d9d7a72015-10-21 10:27:10 -070031 return SkIRect::MakeSize(imageDims).contains(subset);
32}
33
msarett5406d6f2015-08-31 06:55:13 -070034/*
msarett10522ff2015-09-07 08:54:01 -070035 * returns a scaled dimension based on the original dimension and the sampleSize
36 * NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder
msarett3d9d7a72015-10-21 10:27:10 -070037 * FIXME: I think we should call this get_sampled_dimension().
msarett10522ff2015-09-07 08:54:01 -070038 */
msarettd1ec89b2016-08-03 12:59:27 -070039static inline int get_scaled_dimension(int srcDimension, int sampleSize) {
msarett10522ff2015-09-07 08:54:01 -070040 if (sampleSize > srcDimension) {
41 return 1;
42 }
43 return srcDimension / sampleSize;
44}
45
46/*
msarett5406d6f2015-08-31 06:55:13 -070047 * Returns the first coordinate that we will keep during a scaled decode.
48 * The output can be interpreted as an x-coordinate or a y-coordinate.
49 *
50 * This does not need to be called and is not called when sampleFactor == 1.
51 */
msarettd1ec89b2016-08-03 12:59:27 -070052static inline int get_start_coord(int sampleFactor) { return sampleFactor / 2; };
msarett5406d6f2015-08-31 06:55:13 -070053
54/*
55 * Given a coordinate in the original image, this returns the corresponding
56 * coordinate in the scaled image. This function is meaningless if
57 * IsCoordNecessary returns false.
58 * The output can be interpreted as an x-coordinate or a y-coordinate.
59 *
60 * This does not need to be called and is not called when sampleFactor == 1.
61 */
msarettd1ec89b2016-08-03 12:59:27 -070062static inline int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; };
msarett5406d6f2015-08-31 06:55:13 -070063
64/*
65 * When scaling, we will discard certain y-coordinates (rows) and
66 * x-coordinates (columns). This function returns true if we should keep the
67 * coordinate and false otherwise.
68 * The inputs may be x-coordinates or y-coordinates.
69 *
70 * This does not need to be called and is not called when sampleFactor == 1.
71 */
msarettd1ec89b2016-08-03 12:59:27 -070072static inline bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) {
msarett5406d6f2015-08-31 06:55:13 -070073 // Get the first coordinate that we want to keep
74 int startCoord = get_start_coord(sampleFactor);
75
76 // Return false on edge cases
77 if (srcCoord < startCoord || get_dst_coord(srcCoord, sampleFactor) >= scaledDim) {
78 return false;
79 }
80
81 // Every sampleFactor rows are necessary
82 return ((srcCoord - startCoord) % sampleFactor) == 0;
83}
84
msarettd1ec89b2016-08-03 12:59:27 -070085static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
scroggoc5560be2016-02-03 09:42:42 -080086 if (kUnknown_SkAlphaType == dstAlpha) {
87 return false;
88 }
89
msarett4ab9d5f2015-08-06 15:34:42 -070090 if (srcAlpha != dstAlpha) {
91 if (kOpaque_SkAlphaType == srcAlpha) {
scroggoc5560be2016-02-03 09:42:42 -080092 // If the source is opaque, we can support any.
93 SkCodecPrintf("Warning: an opaque image should be decoded as opaque "
94 "- it is being decoded as non-opaque, which will draw slower\n");
95 return true;
msarett4ab9d5f2015-08-06 15:34:42 -070096 }
97
98 // The source is not opaque
99 switch (dstAlpha) {
100 case kPremul_SkAlphaType:
101 case kUnpremul_SkAlphaType:
102 // The source is not opaque, so either of these is okay
103 break;
104 default:
105 // We cannot decode a non-opaque image to opaque (or unknown)
106 return false;
107 }
108 }
109 return true;
110}
111
msarett74114382015-03-16 11:55:18 -0700112/*
halcanary96fcdcc2015-08-27 07:41:13 -0700113 * If there is a color table, get a pointer to the colors, otherwise return nullptr
msarett99f567e2015-08-05 12:58:26 -0700114 */
msarettd1ec89b2016-08-03 12:59:27 -0700115static inline const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
halcanary96fcdcc2015-08-27 07:41:13 -0700116 return nullptr != colorTable ? colorTable->readColors() : nullptr;
msarett99f567e2015-08-05 12:58:26 -0700117}
118
119/*
msarette6dd0042015-10-09 11:07:34 -0700120 * Given that the encoded image uses a color table, return the fill value
121 */
msarettcf7b8772016-09-22 12:37:04 -0700122static inline uint64_t get_color_table_fill_value(SkColorType dstColorType, SkAlphaType alphaType,
Matt Sarett19aff5d2017-04-03 16:01:10 -0400123 const SkPMColor* colorPtr, uint8_t fillIndex, SkColorSpaceXform* colorXform, bool isRGBA) {
msarette6dd0042015-10-09 11:07:34 -0700124 SkASSERT(nullptr != colorPtr);
msarettcf7b8772016-09-22 12:37:04 -0700125 switch (dstColorType) {
msarett34e0ec42016-04-22 16:27:24 -0700126 case kRGBA_8888_SkColorType:
127 case kBGRA_8888_SkColorType:
msarette6dd0042015-10-09 11:07:34 -0700128 return colorPtr[fillIndex];
129 case kRGB_565_SkColorType:
130 return SkPixel32ToPixel16(colorPtr[fillIndex]);
131 case kIndex_8_SkColorType:
132 return fillIndex;
msarettf7eb6fc2016-09-13 09:04:11 -0700133 case kRGBA_F16_SkColorType: {
134 SkASSERT(colorXform);
135 uint64_t dstColor;
136 uint32_t srcColor = colorPtr[fillIndex];
Matt Sarett19aff5d2017-04-03 16:01:10 -0400137 SkColorSpaceXform::ColorFormat srcFormat =
138 isRGBA ? SkColorSpaceXform::kRGBA_8888_ColorFormat
139 : SkColorSpaceXform::kBGRA_8888_ColorFormat;
msarett31d097e82016-10-11 12:15:03 -0700140 SkAssertResult(colorXform->apply(select_xform_format(dstColorType), &dstColor,
Matt Sarett19aff5d2017-04-03 16:01:10 -0400141 srcFormat, &srcColor, 1, alphaType));
msarettf7eb6fc2016-09-13 09:04:11 -0700142 return dstColor;
143 }
msarette6dd0042015-10-09 11:07:34 -0700144 default:
145 SkASSERT(false);
146 return 0;
147 }
148}
149
150/*
msarett74114382015-03-16 11:55:18 -0700151 *
msarett9e43cab2015-04-29 07:38:43 -0700152 * Copy the codec color table back to the client when kIndex8 color type is requested
msarett9e43cab2015-04-29 07:38:43 -0700153 */
msarettd1ec89b2016-08-03 12:59:27 -0700154static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
msarett9e43cab2015-04-29 07:38:43 -0700155 SkPMColor* inputColorPtr, int* inputColorCount) {
156 if (kIndex_8_SkColorType == dstInfo.colorType()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700157 SkASSERT(nullptr != inputColorPtr);
158 SkASSERT(nullptr != inputColorCount);
159 SkASSERT(nullptr != colorTable);
msarett10522ff2015-09-07 08:54:01 -0700160 memcpy(inputColorPtr, colorTable->readColors(), *inputColorCount * sizeof(SkPMColor));
msarett9e43cab2015-04-29 07:38:43 -0700161 }
162}
163
164/*
msarett74114382015-03-16 11:55:18 -0700165 * Compute row bytes for an image using pixels per byte
msarett74114382015-03-16 11:55:18 -0700166 */
msarettd1ec89b2016-08-03 12:59:27 -0700167static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
msarett74114382015-03-16 11:55:18 -0700168 return (width + pixelsPerByte - 1) / pixelsPerByte;
169}
170
171/*
msarett74114382015-03-16 11:55:18 -0700172 * Compute row bytes for an image using bytes per pixel
msarett74114382015-03-16 11:55:18 -0700173 */
msarettd1ec89b2016-08-03 12:59:27 -0700174static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
msarett74114382015-03-16 11:55:18 -0700175 return width * bytesPerPixel;
176}
177
178/*
msarett74114382015-03-16 11:55:18 -0700179 * Compute row bytes for an image
msarett74114382015-03-16 11:55:18 -0700180 */
msarettd1ec89b2016-08-03 12:59:27 -0700181static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
msarett74114382015-03-16 11:55:18 -0700182 if (bitsPerPixel < 16) {
183 SkASSERT(0 == 8 % bitsPerPixel);
184 const uint32_t pixelsPerByte = 8 / bitsPerPixel;
185 return compute_row_bytes_ppb(width, pixelsPerByte);
186 } else {
187 SkASSERT(0 == bitsPerPixel % 8);
188 const uint32_t bytesPerPixel = bitsPerPixel / 8;
189 return compute_row_bytes_bpp(width, bytesPerPixel);
190 }
191}
192
193/*
msarett74114382015-03-16 11:55:18 -0700194 * Get a byte from a buffer
195 * This method is unsafe, the caller is responsible for performing a check
msarett74114382015-03-16 11:55:18 -0700196 */
msarettd1ec89b2016-08-03 12:59:27 -0700197static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
msarett74114382015-03-16 11:55:18 -0700198 return buffer[i];
199}
200
201/*
msarett74114382015-03-16 11:55:18 -0700202 * Get a short from a buffer
203 * This method is unsafe, the caller is responsible for performing a check
msarett74114382015-03-16 11:55:18 -0700204 */
msarettd1ec89b2016-08-03 12:59:27 -0700205static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
msarett74114382015-03-16 11:55:18 -0700206 uint16_t result;
207 memcpy(&result, &(buffer[i]), 2);
208#ifdef SK_CPU_BENDIAN
209 return SkEndianSwap16(result);
210#else
211 return result;
212#endif
213}
214
215/*
msarett74114382015-03-16 11:55:18 -0700216 * Get an int from a buffer
217 * This method is unsafe, the caller is responsible for performing a check
msarett74114382015-03-16 11:55:18 -0700218 */
msarettd1ec89b2016-08-03 12:59:27 -0700219static inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
msarett74114382015-03-16 11:55:18 -0700220 uint32_t result;
221 memcpy(&result, &(buffer[i]), 4);
222#ifdef SK_CPU_BENDIAN
223 return SkEndianSwap32(result);
224#else
225 return result;
226#endif
227}
228
msarett0e6274f2016-03-21 08:04:40 -0700229/*
230 * @param data Buffer to read bytes from
231 * @param isLittleEndian Output parameter
232 * Indicates if the data is little endian
233 * Is unaffected on false returns
234 */
msarettd1ec89b2016-08-03 12:59:27 -0700235static inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) {
msarett0e6274f2016-03-21 08:04:40 -0700236 // II indicates Intel (little endian) and MM indicates motorola (big endian).
237 if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) {
238 return false;
239 }
240
241 *isLittleEndian = ('I' == data[0]);
242 return true;
243}
244
msarettd1ec89b2016-08-03 12:59:27 -0700245static inline uint16_t get_endian_short(const uint8_t* data, bool littleEndian) {
msarett0e6274f2016-03-21 08:04:40 -0700246 if (littleEndian) {
247 return (data[1] << 8) | (data[0]);
248 }
249
250 return (data[0] << 8) | (data[1]);
251}
252
msarettd1ec89b2016-08-03 12:59:27 -0700253static inline SkPMColor premultiply_argb_as_rgba(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
msarett34e0ec42016-04-22 16:27:24 -0700254 if (a != 255) {
255 r = SkMulDiv255Round(r, a);
256 g = SkMulDiv255Round(g, a);
257 b = SkMulDiv255Round(b, a);
258 }
259
260 return SkPackARGB_as_RGBA(a, r, g, b);
261}
262
msarettd1ec89b2016-08-03 12:59:27 -0700263static inline SkPMColor premultiply_argb_as_bgra(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
msarett34e0ec42016-04-22 16:27:24 -0700264 if (a != 255) {
265 r = SkMulDiv255Round(r, a);
266 g = SkMulDiv255Round(g, a);
267 b = SkMulDiv255Round(b, a);
268 }
269
270 return SkPackARGB_as_BGRA(a, r, g, b);
271}
272
msarettd1ec89b2016-08-03 12:59:27 -0700273static inline bool is_rgba(SkColorType colorType) {
msarett34e0ec42016-04-22 16:27:24 -0700274#ifdef SK_PMCOLOR_IS_RGBA
275 return (kBGRA_8888_SkColorType != colorType);
276#else
277 return (kRGBA_8888_SkColorType == colorType);
278#endif
279}
280
281// Method for coverting to a 32 bit pixel.
282typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
283
msarettd1ec89b2016-08-03 12:59:27 -0700284static inline PackColorProc choose_pack_color_proc(bool isPremul, SkColorType colorType) {
msarett34e0ec42016-04-22 16:27:24 -0700285 bool isRGBA = is_rgba(colorType);
286 if (isPremul) {
287 if (isRGBA) {
288 return &premultiply_argb_as_rgba;
289 } else {
290 return &premultiply_argb_as_bgra;
291 }
292 } else {
293 if (isRGBA) {
294 return &SkPackARGB_as_RGBA;
295 } else {
296 return &SkPackARGB_as_BGRA;
297 }
298 }
299}
300
Matt Sarett61eedeb2016-11-04 13:19:48 -0400301static inline bool needs_premul(const SkImageInfo& dstInfo, const SkEncodedInfo& encodedInfo) {
msarettdcd5e652016-08-22 08:48:40 -0700302 return kPremul_SkAlphaType == dstInfo.alphaType() &&
Matt Sarett61eedeb2016-11-04 13:19:48 -0400303 SkEncodedInfo::kUnpremul_Alpha == encodedInfo.alpha();
msarettdcd5e652016-08-22 08:48:40 -0700304}
305
Matt Sarett61eedeb2016-11-04 13:19:48 -0400306static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo,
Matt Sarettcf3f2342017-03-23 15:32:25 -0400307 bool needsColorCorrectPremul) {
Matt Sarette522f4c2017-02-22 13:02:31 -0500308 // We never perform a color xform in legacy mode.
309 if (!dstInfo.colorSpace()) {
310 return false;
311 }
312
msarettdcd5e652016-08-22 08:48:40 -0700313 // F16 is by definition a linear space, so we always must perform a color xform.
314 bool isF16 = kRGBA_F16_SkColorType == dstInfo.colorType();
315
316 // Need a color xform when dst space does not match the src.
Matt Sarettf3880932017-03-24 10:06:03 -0400317 bool srcDstNotEqual = !SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace());
msarettdcd5e652016-08-22 08:48:40 -0700318
Matt Sarette522f4c2017-02-22 13:02:31 -0500319 return needsColorCorrectPremul || isF16 || srcDstNotEqual;
msarettd1ec89b2016-08-03 12:59:27 -0700320}
321
msarettc0444612016-09-16 11:45:58 -0700322static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaType srcAlphaType) {
msarette99883f2016-09-08 06:05:35 -0700323 return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType;
324}
325
Matt Sarett1a85ca52016-11-04 11:52:48 -0400326static inline bool apply_xform_on_decode(SkColorType dstColorType, SkEncodedInfo::Color srcColor) {
Matt Sarett19aff5d2017-04-03 16:01:10 -0400327 // We will apply the color xform when reading the color table unless F16 is requested.
328 return SkEncodedInfo::kPalette_Color != srcColor || kRGBA_F16_SkColorType == dstColorType;
Matt Sarett1a85ca52016-11-04 11:52:48 -0400329}
330
msarett2ecc35f2016-09-08 11:55:16 -0700331/*
332 * Alpha Type Conversions
333 * - kOpaque to kOpaque, kUnpremul, kPremul is valid
334 * - kUnpremul to kUnpremul, kPremul is valid
335 *
336 * Color Type Conversions
337 * - Always support kRGBA_8888, kBGRA_8888
338 * - Support kRGBA_F16 when there is a linear dst color space
339 * - Support kIndex8 if it matches the src
340 * - Support k565 if kOpaque and color correction is not required
341 * - Support k565 if it matches the src, kOpaque, and color correction is not required
342 */
343static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
344 // Ensure the alpha type is valid.
345 if (!valid_alpha(dst.alphaType(), src.alphaType())) {
346 return false;
347 }
348
349 // Check for supported color types.
350 switch (dst.colorType()) {
351 case kRGBA_8888_SkColorType:
352 case kBGRA_8888_SkColorType:
353 return true;
354 case kRGBA_F16_SkColorType:
355 return dst.colorSpace() && dst.colorSpace()->gammaIsLinear();
356 case kIndex_8_SkColorType:
357 return kIndex_8_SkColorType == src.colorType();
358 case kRGB_565_SkColorType:
Matt Sarett3725f0a2017-03-28 14:34:20 -0400359 return kOpaque_SkAlphaType == src.alphaType();
msarett2ecc35f2016-09-08 11:55:16 -0700360 case kGray_8_SkColorType:
361 return kGray_8_SkColorType == src.colorType() &&
Matt Sarett61eedeb2016-11-04 13:19:48 -0400362 kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src, false);
msarett2ecc35f2016-09-08 11:55:16 -0700363 default:
364 return false;
365 }
366}
367
Matt Sarett19aff5d2017-04-03 16:01:10 -0400368static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType colorType) {
369 switch (colorType) {
370 case kRGBA_8888_SkColorType:
371 return SkColorSpaceXform::kRGBA_8888_ColorFormat;
372 case kBGRA_8888_SkColorType:
373 return SkColorSpaceXform::kBGRA_8888_ColorFormat;
374 case kRGB_565_SkColorType:
375 case kIndex_8_SkColorType:
376#ifdef SK_PMCOLOR_IS_RGBA
377 return SkColorSpaceXform::kRGBA_8888_ColorFormat;
378#else
379 return SkColorSpaceXform::kBGRA_8888_ColorFormat;
380#endif
381 default:
382 SkASSERT(false);
383 return SkColorSpaceXform::kRGBA_8888_ColorFormat;
384 }
385}
386
msarett74114382015-03-16 11:55:18 -0700387#endif // SkCodecPriv_DEFINED