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