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