SkCodecPriv.h revision cf7b877d62537672b67449bc96858cc1262be5f8
1/* 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 11#include "SkColorPriv.h" 12#include "SkColorSpaceXform.h" 13#include "SkColorTable.h" 14#include "SkImageInfo.h" 15#include "SkTypes.h" 16 17#ifdef SK_PRINT_CODEC_MESSAGES 18 #define SkCodecPrintf SkDebugf 19#else 20 #define SkCodecPrintf(...) 21#endif 22 23// FIXME: Consider sharing with dm, nanbench, and tools. 24static inline float get_scale_from_sample_size(int sampleSize) { 25 return 1.0f / ((float) sampleSize); 26} 27 28static inline bool is_valid_subset(const SkIRect& subset, const SkISize& imageDims) { 29 return SkIRect::MakeSize(imageDims).contains(subset); 30} 31 32/* 33 * returns a scaled dimension based on the original dimension and the sampleSize 34 * NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder 35 * FIXME: I think we should call this get_sampled_dimension(). 36 */ 37static inline int get_scaled_dimension(int srcDimension, int sampleSize) { 38 if (sampleSize > srcDimension) { 39 return 1; 40 } 41 return srcDimension / sampleSize; 42} 43 44/* 45 * Returns the first coordinate that we will keep during a scaled decode. 46 * The output can be interpreted as an x-coordinate or a y-coordinate. 47 * 48 * This does not need to be called and is not called when sampleFactor == 1. 49 */ 50static inline int get_start_coord(int sampleFactor) { return sampleFactor / 2; }; 51 52/* 53 * Given a coordinate in the original image, this returns the corresponding 54 * coordinate in the scaled image. This function is meaningless if 55 * IsCoordNecessary returns false. 56 * The output can be interpreted as an x-coordinate or a y-coordinate. 57 * 58 * This does not need to be called and is not called when sampleFactor == 1. 59 */ 60static inline int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; }; 61 62/* 63 * When scaling, we will discard certain y-coordinates (rows) and 64 * x-coordinates (columns). This function returns true if we should keep the 65 * coordinate and false otherwise. 66 * The inputs may be x-coordinates or y-coordinates. 67 * 68 * This does not need to be called and is not called when sampleFactor == 1. 69 */ 70static inline bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) { 71 // Get the first coordinate that we want to keep 72 int startCoord = get_start_coord(sampleFactor); 73 74 // Return false on edge cases 75 if (srcCoord < startCoord || get_dst_coord(srcCoord, sampleFactor) >= scaledDim) { 76 return false; 77 } 78 79 // Every sampleFactor rows are necessary 80 return ((srcCoord - startCoord) % sampleFactor) == 0; 81} 82 83static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) { 84 if (kUnknown_SkAlphaType == dstAlpha) { 85 return false; 86 } 87 88 if (srcAlpha != dstAlpha) { 89 if (kOpaque_SkAlphaType == srcAlpha) { 90 // If the source is opaque, we can support any. 91 SkCodecPrintf("Warning: an opaque image should be decoded as opaque " 92 "- it is being decoded as non-opaque, which will draw slower\n"); 93 return true; 94 } 95 96 // The source is not opaque 97 switch (dstAlpha) { 98 case kPremul_SkAlphaType: 99 case kUnpremul_SkAlphaType: 100 // The source is not opaque, so either of these is okay 101 break; 102 default: 103 // We cannot decode a non-opaque image to opaque (or unknown) 104 return false; 105 } 106 } 107 return true; 108} 109 110/* 111 * Original version of conversion_possible that does not account for color spaces. 112 * Used by codecs that have not been updated to support color spaces. 113 * 114 * Most of our codecs support the same conversions: 115 * - opaque to any alpha type 116 * - 565 only if opaque 117 * - premul to unpremul and vice versa 118 * - always support RGBA, BGRA 119 * - otherwise match the src color type 120 */ 121static inline bool conversion_possible_ignore_color_space(const SkImageInfo& dst, 122 const SkImageInfo& src) { 123 // Ensure the alpha type is valid 124 if (!valid_alpha(dst.alphaType(), src.alphaType())) { 125 return false; 126 } 127 128 // Check for supported color types 129 switch (dst.colorType()) { 130 case kRGBA_8888_SkColorType: 131 case kBGRA_8888_SkColorType: 132 return true; 133 case kRGB_565_SkColorType: 134 return kOpaque_SkAlphaType == src.alphaType(); 135 default: 136 return dst.colorType() == src.colorType(); 137 } 138} 139 140/* 141 * If there is a color table, get a pointer to the colors, otherwise return nullptr 142 */ 143static inline const SkPMColor* get_color_ptr(SkColorTable* colorTable) { 144 return nullptr != colorTable ? colorTable->readColors() : nullptr; 145} 146 147static inline SkColorSpaceXform::ColorFormat select_xform_format(SkColorType colorType) { 148 switch (colorType) { 149 case kRGBA_8888_SkColorType: 150 return SkColorSpaceXform::kRGBA_8888_ColorFormat; 151 case kBGRA_8888_SkColorType: 152 return SkColorSpaceXform::kBGRA_8888_ColorFormat; 153 case kRGBA_F16_SkColorType: 154 return SkColorSpaceXform::kRGBA_F16_ColorFormat; 155 default: 156 SkASSERT(false); 157 return SkColorSpaceXform::kRGBA_8888_ColorFormat; 158 } 159} 160 161/* 162 * Given that the encoded image uses a color table, return the fill value 163 */ 164static inline uint64_t get_color_table_fill_value(SkColorType dstColorType, SkAlphaType alphaType, 165 const SkPMColor* colorPtr, uint8_t fillIndex, SkColorSpaceXform* colorXform) { 166 SkASSERT(nullptr != colorPtr); 167 switch (dstColorType) { 168 case kRGBA_8888_SkColorType: 169 case kBGRA_8888_SkColorType: 170 return colorPtr[fillIndex]; 171 case kRGB_565_SkColorType: 172 return SkPixel32ToPixel16(colorPtr[fillIndex]); 173 case kIndex_8_SkColorType: 174 return fillIndex; 175 case kRGBA_F16_SkColorType: { 176 SkASSERT(colorXform); 177 uint64_t dstColor; 178 uint32_t srcColor = colorPtr[fillIndex]; 179 colorXform->apply(&dstColor, &srcColor, 1, select_xform_format(dstColorType), 180 SkColorSpaceXform::kRGBA_8888_ColorFormat, alphaType); 181 return dstColor; 182 } 183 default: 184 SkASSERT(false); 185 return 0; 186 } 187} 188 189/* 190 * 191 * Copy the codec color table back to the client when kIndex8 color type is requested 192 */ 193static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable, 194 SkPMColor* inputColorPtr, int* inputColorCount) { 195 if (kIndex_8_SkColorType == dstInfo.colorType()) { 196 SkASSERT(nullptr != inputColorPtr); 197 SkASSERT(nullptr != inputColorCount); 198 SkASSERT(nullptr != colorTable); 199 memcpy(inputColorPtr, colorTable->readColors(), *inputColorCount * sizeof(SkPMColor)); 200 } 201} 202 203/* 204 * Compute row bytes for an image using pixels per byte 205 */ 206static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) { 207 return (width + pixelsPerByte - 1) / pixelsPerByte; 208} 209 210/* 211 * Compute row bytes for an image using bytes per pixel 212 */ 213static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) { 214 return width * bytesPerPixel; 215} 216 217/* 218 * Compute row bytes for an image 219 */ 220static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) { 221 if (bitsPerPixel < 16) { 222 SkASSERT(0 == 8 % bitsPerPixel); 223 const uint32_t pixelsPerByte = 8 / bitsPerPixel; 224 return compute_row_bytes_ppb(width, pixelsPerByte); 225 } else { 226 SkASSERT(0 == bitsPerPixel % 8); 227 const uint32_t bytesPerPixel = bitsPerPixel / 8; 228 return compute_row_bytes_bpp(width, bytesPerPixel); 229 } 230} 231 232/* 233 * Get a byte from a buffer 234 * This method is unsafe, the caller is responsible for performing a check 235 */ 236static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) { 237 return buffer[i]; 238} 239 240/* 241 * Get a short from a buffer 242 * This method is unsafe, the caller is responsible for performing a check 243 */ 244static inline uint16_t get_short(uint8_t* buffer, uint32_t i) { 245 uint16_t result; 246 memcpy(&result, &(buffer[i]), 2); 247#ifdef SK_CPU_BENDIAN 248 return SkEndianSwap16(result); 249#else 250 return result; 251#endif 252} 253 254/* 255 * Get an int from a buffer 256 * This method is unsafe, the caller is responsible for performing a check 257 */ 258static inline uint32_t get_int(uint8_t* buffer, uint32_t i) { 259 uint32_t result; 260 memcpy(&result, &(buffer[i]), 4); 261#ifdef SK_CPU_BENDIAN 262 return SkEndianSwap32(result); 263#else 264 return result; 265#endif 266} 267 268/* 269 * @param data Buffer to read bytes from 270 * @param isLittleEndian Output parameter 271 * Indicates if the data is little endian 272 * Is unaffected on false returns 273 */ 274static inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) { 275 // II indicates Intel (little endian) and MM indicates motorola (big endian). 276 if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) { 277 return false; 278 } 279 280 *isLittleEndian = ('I' == data[0]); 281 return true; 282} 283 284static inline uint16_t get_endian_short(const uint8_t* data, bool littleEndian) { 285 if (littleEndian) { 286 return (data[1] << 8) | (data[0]); 287 } 288 289 return (data[0] << 8) | (data[1]); 290} 291 292static inline SkPMColor premultiply_argb_as_rgba(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { 293 if (a != 255) { 294 r = SkMulDiv255Round(r, a); 295 g = SkMulDiv255Round(g, a); 296 b = SkMulDiv255Round(b, a); 297 } 298 299 return SkPackARGB_as_RGBA(a, r, g, b); 300} 301 302static inline SkPMColor premultiply_argb_as_bgra(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { 303 if (a != 255) { 304 r = SkMulDiv255Round(r, a); 305 g = SkMulDiv255Round(g, a); 306 b = SkMulDiv255Round(b, a); 307 } 308 309 return SkPackARGB_as_BGRA(a, r, g, b); 310} 311 312static inline bool is_rgba(SkColorType colorType) { 313#ifdef SK_PMCOLOR_IS_RGBA 314 return (kBGRA_8888_SkColorType != colorType); 315#else 316 return (kRGBA_8888_SkColorType == colorType); 317#endif 318} 319 320// Method for coverting to a 32 bit pixel. 321typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); 322 323static inline PackColorProc choose_pack_color_proc(bool isPremul, SkColorType colorType) { 324 bool isRGBA = is_rgba(colorType); 325 if (isPremul) { 326 if (isRGBA) { 327 return &premultiply_argb_as_rgba; 328 } else { 329 return &premultiply_argb_as_bgra; 330 } 331 } else { 332 if (isRGBA) { 333 return &SkPackARGB_as_RGBA; 334 } else { 335 return &SkPackARGB_as_BGRA; 336 } 337 } 338} 339 340static inline bool needs_premul(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) { 341 return kPremul_SkAlphaType == dstInfo.alphaType() && 342 kUnpremul_SkAlphaType == srcInfo.alphaType(); 343} 344 345static inline bool needs_color_xform(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo) { 346 // Color xform is necessary in order to correctly perform premultiply in linear space. 347 bool needsPremul = needs_premul(dstInfo, srcInfo); 348 349 // F16 is by definition a linear space, so we always must perform a color xform. 350 bool isF16 = kRGBA_F16_SkColorType == dstInfo.colorType(); 351 352 // Need a color xform when dst space does not match the src. 353 bool srcDstNotEqual = !SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace()); 354 355 // We never perform a color xform in legacy mode. 356 bool isLegacy = nullptr == dstInfo.colorSpace(); 357 358 return !isLegacy && (needsPremul || isF16 || srcDstNotEqual); 359} 360 361static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaType srcAlphaType) { 362 return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType; 363} 364 365/* 366 * Alpha Type Conversions 367 * - kOpaque to kOpaque, kUnpremul, kPremul is valid 368 * - kUnpremul to kUnpremul, kPremul is valid 369 * 370 * Color Type Conversions 371 * - Always support kRGBA_8888, kBGRA_8888 372 * - Support kRGBA_F16 when there is a linear dst color space 373 * - Support kIndex8 if it matches the src 374 * - Support k565 if kOpaque and color correction is not required 375 * - Support k565 if it matches the src, kOpaque, and color correction is not required 376 */ 377static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { 378 // Ensure the alpha type is valid. 379 if (!valid_alpha(dst.alphaType(), src.alphaType())) { 380 return false; 381 } 382 383 // Check for supported color types. 384 switch (dst.colorType()) { 385 case kRGBA_8888_SkColorType: 386 case kBGRA_8888_SkColorType: 387 return true; 388 case kRGBA_F16_SkColorType: 389 return dst.colorSpace() && dst.colorSpace()->gammaIsLinear(); 390 case kIndex_8_SkColorType: 391 return kIndex_8_SkColorType == src.colorType(); 392 case kRGB_565_SkColorType: 393 return kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src); 394 case kGray_8_SkColorType: 395 return kGray_8_SkColorType == src.colorType() && 396 kOpaque_SkAlphaType == src.alphaType() && !needs_color_xform(dst, src); 397 default: 398 return false; 399 } 400} 401 402#endif // SkCodecPriv_DEFINED 403