| 1 | /* |
| 2 | * Copyright 2007 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 SkBitmapProcState_DEFINED |
| 9 | #define SkBitmapProcState_DEFINED |
| 10 | |
| 11 | #include "include/core/SkBitmap.h" |
| 12 | #include "include/core/SkPaint.h" |
| 13 | #include "include/core/SkShader.h" |
| 14 | #include "include/private/SkFixed.h" |
| 15 | #include "include/private/SkFloatBits.h" |
| 16 | #include "include/private/SkTemplates.h" |
| 17 | #include "src/core/SkArenaAlloc.h" |
| 18 | #include "src/core/SkBitmapController.h" |
| 19 | #include "src/core/SkMatrixPriv.h" |
| 20 | |
| 21 | typedef SkFixed3232 SkFractionalInt; |
| 22 | #define SkScalarToFractionalInt(x) SkScalarToFixed3232(x) |
| 23 | #define SkFractionalIntToFixed(x) SkFixed3232ToFixed(x) |
| 24 | #define SkFixedToFractionalInt(x) SkFixedToFixed3232(x) |
| 25 | #define SkFractionalIntToInt(x) SkFixed3232ToInt(x) |
| 26 | |
| 27 | class SkPaint; |
| 28 | |
| 29 | struct SkBitmapProcInfo { |
| 30 | SkBitmapProcInfo(const SkImage_Base*, SkTileMode tmx, SkTileMode tmy); |
| 31 | ~SkBitmapProcInfo(); |
| 32 | |
| 33 | const SkImage_Base* fImage; |
| 34 | |
| 35 | SkPixmap fPixmap; |
| 36 | SkMatrix fInvMatrix; // This changes based on tile mode. |
| 37 | SkColor fPaintColor; |
| 38 | SkTileMode fTileModeX; |
| 39 | SkTileMode fTileModeY; |
| 40 | SkFilterQuality fFilterQuality; |
| 41 | |
| 42 | bool init(const SkMatrix& inverse, const SkPaint&); |
| 43 | |
| 44 | private: |
| 45 | enum { |
| 46 | kBMStateSize = 136 // found by inspection. if too small, we will call new/delete |
| 47 | }; |
| 48 | SkSTArenaAlloc<kBMStateSize> fAlloc; |
| 49 | SkBitmapController::State* fBMState; |
| 50 | }; |
| 51 | |
| 52 | struct SkBitmapProcState : public SkBitmapProcInfo { |
| 53 | SkBitmapProcState(const SkImage_Base* image, SkTileMode tmx, SkTileMode tmy) |
| 54 | : SkBitmapProcInfo(image, tmx, tmy) {} |
| 55 | |
| 56 | bool setup(const SkMatrix& inv, const SkPaint& paint) { |
| 57 | return this->init(inv, paint) && this->chooseProcs(); |
| 58 | } |
| 59 | |
| 60 | typedef void (*ShaderProc32)(const void* ctx, int x, int y, SkPMColor[], int count); |
| 61 | |
| 62 | typedef void (*MatrixProc)(const SkBitmapProcState&, |
| 63 | uint32_t bitmapXY[], |
| 64 | int count, |
| 65 | int x, int y); |
| 66 | |
| 67 | typedef void (*SampleProc32)(const SkBitmapProcState&, |
| 68 | const uint32_t[], |
| 69 | int count, |
| 70 | SkPMColor colors[]); |
| 71 | |
| 72 | SkMatrixPriv::MapXYProc fInvProc; // chooseProcs |
| 73 | SkFractionalInt fInvSxFractionalInt; |
| 74 | SkFractionalInt fInvKyFractionalInt; |
| 75 | |
| 76 | SkFixed fFilterOneX; |
| 77 | SkFixed fFilterOneY; |
| 78 | |
| 79 | uint16_t fAlphaScale; // chooseProcs |
| 80 | |
| 81 | /** Given the byte size of the index buffer to be passed to the matrix proc, |
| 82 | return the maximum number of resulting pixels that can be computed |
| 83 | (i.e. the number of SkPMColor values to be written by the sample proc). |
| 84 | This routine takes into account that filtering and scale-vs-affine |
| 85 | affect the amount of buffer space needed. |
| 86 | |
| 87 | Only valid to call after chooseProcs (setContext) has been called. It is |
| 88 | safe to call this inside the shader's shadeSpan() method. |
| 89 | */ |
| 90 | int maxCountForBufferSize(size_t bufferSize) const; |
| 91 | |
| 92 | // If a shader proc is present, then the corresponding matrix/sample procs |
| 93 | // are ignored |
| 94 | ShaderProc32 getShaderProc32() const { return fShaderProc32; } |
| 95 | |
| 96 | #ifdef SK_DEBUG |
| 97 | MatrixProc getMatrixProc() const; |
| 98 | #else |
| 99 | MatrixProc getMatrixProc() const { return fMatrixProc; } |
| 100 | #endif |
| 101 | SampleProc32 getSampleProc32() const { return fSampleProc32; } |
| 102 | |
| 103 | private: |
| 104 | ShaderProc32 fShaderProc32; // chooseProcs |
| 105 | // These are used if the shaderproc is nullptr |
| 106 | MatrixProc fMatrixProc; // chooseProcs |
| 107 | SampleProc32 fSampleProc32; // chooseProcs |
| 108 | |
| 109 | MatrixProc chooseMatrixProc(bool trivial_matrix); |
| 110 | bool chooseProcs(); // caller must have called init() first (on our base-class) |
| 111 | ShaderProc32 chooseShaderProc32(); |
| 112 | |
| 113 | // Return false if we failed to setup for fast translate (e.g. overflow) |
| 114 | bool setupForTranslate(); |
| 115 | |
| 116 | #ifdef SK_DEBUG |
| 117 | static void DebugMatrixProc(const SkBitmapProcState&, |
| 118 | uint32_t[], int count, int x, int y); |
| 119 | #endif |
| 120 | }; |
| 121 | |
| 122 | /* Macros for packing and unpacking pairs of 16bit values in a 32bit uint. |
| 123 | Used to allow access to a stream of uint16_t either one at a time, or |
| 124 | 2 at a time by unpacking a uint32_t |
| 125 | */ |
| 126 | #ifdef SK_CPU_BENDIAN |
| 127 | #define PACK_TWO_SHORTS(pri, sec) ((pri) << 16 | (sec)) |
| 128 | #define UNPACK_PRIMARY_SHORT(packed) ((uint32_t)(packed) >> 16) |
| 129 | #define UNPACK_SECONDARY_SHORT(packed) ((packed) & 0xFFFF) |
| 130 | #else |
| 131 | #define PACK_TWO_SHORTS(pri, sec) ((pri) | ((sec) << 16)) |
| 132 | #define UNPACK_PRIMARY_SHORT(packed) ((packed) & 0xFFFF) |
| 133 | #define UNPACK_SECONDARY_SHORT(packed) ((uint32_t)(packed) >> 16) |
| 134 | #endif |
| 135 | |
| 136 | #ifdef SK_DEBUG |
| 137 | static inline uint32_t pack_two_shorts(U16CPU pri, U16CPU sec) { |
| 138 | SkASSERT((uint16_t)pri == pri); |
| 139 | SkASSERT((uint16_t)sec == sec); |
| 140 | return PACK_TWO_SHORTS(pri, sec); |
| 141 | } |
| 142 | #else |
| 143 | #define pack_two_shorts(pri, sec) PACK_TWO_SHORTS(pri, sec) |
| 144 | #endif |
| 145 | |
| 146 | // Helper class for mapping the middle of pixel (x, y) into SkFractionalInt bitmap space. |
| 147 | // Discussion: |
| 148 | // Overall, this code takes a point in destination space, and uses the center of the pixel |
| 149 | // at (x, y) to determine the sample point in source space. It then adjusts the pixel by different |
| 150 | // amounts based in filtering and tiling. |
| 151 | // This code can be broken into two main cases based on filtering: |
| 152 | // * no filtering (nearest neighbor) - when using nearest neighbor filtering all tile modes reduce |
| 153 | // the sampled by one ulp. If a simple point pt lies precisely on XXX.1/2 then it forced down |
| 154 | // when positive making 1/2 + 1/2 = .999999 instead of 1.0. |
| 155 | // * filtering - in the filtering case, the code calculates the -1/2 shift for starting the |
| 156 | // bilerp kernel. There is a twist; there is a big difference between clamp and the other tile |
| 157 | // modes. In tile and repeat the matrix has been reduced by an additional 1/width and 1/height |
| 158 | // factor. This maps from destination space to [0, 1) (instead of source space) to allow easy |
| 159 | // modulo arithmetic. This means that the -1/2 needed by bilerp is actually 1/2 * 1/width for x |
| 160 | // and 1/2 * 1/height for y. This is what happens when the poorly named fFilterOne{X|Y} is |
| 161 | // divided by two. |
| 162 | class SkBitmapProcStateAutoMapper { |
| 163 | public: |
| 164 | SkBitmapProcStateAutoMapper(const SkBitmapProcState& s, int x, int y, |
| 165 | SkPoint* scalarPoint = nullptr) { |
| 166 | SkPoint pt; |
| 167 | s.fInvProc(s.fInvMatrix, |
| 168 | SkIntToScalar(x) + SK_ScalarHalf, |
| 169 | SkIntToScalar(y) + SK_ScalarHalf, &pt); |
| 170 | |
| 171 | SkFixed biasX, biasY; |
| 172 | if (s.fFilterQuality == kNone_SkFilterQuality) { |
| 173 | // SkFixed epsilon bias to ensure inverse-mapped bitmap coordinates are rounded |
| 174 | // consistently WRT geometry. Note that we only need the bias for positive scales: |
| 175 | // for negative scales, the rounding is intrinsically correct. |
| 176 | // We scale it to persist SkFractionalInt -> SkFixed conversions. |
| 177 | biasX = (s.fInvMatrix.getScaleX() > 0); |
| 178 | biasY = (s.fInvMatrix.getScaleY() > 0); |
| 179 | } else { |
| 180 | biasX = s.fFilterOneX >> 1; |
| 181 | biasY = s.fFilterOneY >> 1; |
| 182 | } |
| 183 | |
| 184 | // punt to unsigned for defined underflow behavior |
| 185 | fX = (SkFractionalInt)((uint64_t)SkScalarToFractionalInt(pt.x()) - |
| 186 | (uint64_t)SkFixedToFractionalInt(biasX)); |
| 187 | fY = (SkFractionalInt)((uint64_t)SkScalarToFractionalInt(pt.y()) - |
| 188 | (uint64_t)SkFixedToFractionalInt(biasY)); |
| 189 | |
| 190 | if (scalarPoint) { |
| 191 | scalarPoint->set(pt.x() - SkFixedToScalar(biasX), |
| 192 | pt.y() - SkFixedToScalar(biasY)); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | SkFractionalInt fractionalIntX() const { return fX; } |
| 197 | SkFractionalInt fractionalIntY() const { return fY; } |
| 198 | |
| 199 | SkFixed fixedX() const { return SkFractionalIntToFixed(fX); } |
| 200 | SkFixed fixedY() const { return SkFractionalIntToFixed(fY); } |
| 201 | |
| 202 | int intX() const { return SkFractionalIntToInt(fX); } |
| 203 | int intY() const { return SkFractionalIntToInt(fY); } |
| 204 | |
| 205 | private: |
| 206 | SkFractionalInt fX, fY; |
| 207 | }; |
| 208 | |
| 209 | #endif |
| 210 | |