1/* libs/graphics/sgl/SkBitmapSampler.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkBitmapSampler.h"
19
20static SkTileModeProc get_tilemode_proc(SkShader::TileMode mode)
21{
22    switch (mode) {
23    case SkShader::kClamp_TileMode:
24        return do_clamp;
25    case SkShader::kRepeat_TileMode:
26        return do_repeat_mod;
27    case SkShader::kMirror_TileMode:
28        return do_mirror_mod;
29    default:
30        SkASSERT(!"unknown mode");
31        return NULL;
32    }
33}
34
35SkBitmapSampler::SkBitmapSampler(const SkBitmap& bm, bool filter,
36                                 SkShader::TileMode tmx, SkShader::TileMode tmy)
37    : fBitmap(bm), fFilterBitmap(filter), fTileModeX(tmx), fTileModeY(tmy)
38{
39    SkASSERT(bm.width() > 0 && bm.height() > 0);
40
41    fMaxX = SkToU16(bm.width() - 1);
42    fMaxY = SkToU16(bm.height() - 1);
43
44    fTileProcX = get_tilemode_proc(tmx);
45    fTileProcY = get_tilemode_proc(tmy);
46}
47
48void SkBitmapSampler::setPaint(const SkPaint& paint)
49{
50}
51
52class SkNullBitmapSampler : public SkBitmapSampler {
53public:
54    SkNullBitmapSampler(const SkBitmap& bm, bool filter,
55                        SkShader::TileMode tmx, SkShader::TileMode tmy)
56        : SkBitmapSampler(bm, filter, tmx, tmy) {}
57
58    virtual SkPMColor sample(SkFixed x, SkFixed y) const { return 0; }
59};
60
61/////////////////////////////////////////////////////////////////////////////////
62/////////////////////////////////////////////////////////////////////////////////
63
64#define BITMAP_CLASSNAME_PREFIX(name)           ARGB32##name
65#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   *bitmap.getAddr32(x, y)
66#include "SkBitmapSamplerTemplate.h"
67
68#include "SkColorPriv.h"
69
70#define BITMAP_CLASSNAME_PREFIX(name)           RGB16##name
71#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   SkPixel16ToPixel32(*bitmap.getAddr16(x, y))
72#include "SkBitmapSamplerTemplate.h"
73
74#define BITMAP_CLASSNAME_PREFIX(name)           Index8##name
75#define BITMAP_PIXEL_TO_PMCOLOR(bitmap, x, y)   bitmap.getIndex8Color(x, y)
76#include "SkBitmapSamplerTemplate.h"
77
78/////////////////////////////////////////////////////////////////////////////////
79/////////////////////////////////////////////////////////////////////////////////
80///////////////// The Bilinear versions
81
82#include "SkFilterProc.h"
83
84class ARGB32_Bilinear_Sampler : public SkBitmapSampler {
85public:
86    ARGB32_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
87        : SkBitmapSampler(bm, true, tmx, tmy)
88    {
89        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
90    }
91
92    virtual SkPMColor sample(SkFixed x, SkFixed y) const
93    {
94        const uint32_t *p00, *p01, *p10, *p11;
95
96        // turn pixel centers into the top-left of our filter-box
97        x -= SK_FixedHalf;
98        y -= SK_FixedHalf;
99
100        // compute our pointers
101        {
102            const SkBitmap* bitmap = &fBitmap;
103            int ix = x >> 16;
104            int iy = y >> 16;
105
106            int             maxX = fMaxX;
107            SkTileModeProc  procX = fTileProcX;
108            int             maxY = fMaxY;
109            SkTileModeProc  procY = fTileProcY;
110
111            int tmpx = procX(ix, maxX);
112            int tmpy = procY(iy, maxY);
113            p00 = bitmap->getAddr32(tmpx, tmpy);
114
115            int tmpx1 = procX(ix + 1, maxX);
116            p01 = bitmap->getAddr32(tmpx1, tmpy);
117
118            int tmpy1 = procY(iy + 1, maxY);
119            p10 = bitmap->getAddr32(tmpx, tmpy1);
120
121            p11 = bitmap->getAddr32(tmpx1, tmpy1);
122        }
123
124        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
125        return proc(p00, p01, p10, p11);
126    }
127
128private:
129    const SkFilterPtrProc* fPtrProcTable;
130};
131
132class RGB16_Bilinear_Sampler : public SkBitmapSampler {
133public:
134    RGB16_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
135        : SkBitmapSampler(bm, true, tmx, tmy)
136    {
137        fProcTable = SkGetBilinearFilterProcTable();
138    }
139
140    virtual SkPMColor sample(SkFixed x, SkFixed y) const
141    {
142        const uint16_t *p00, *p01, *p10, *p11;
143
144        // turn pixel centers into the top-left of our filter-box
145        x -= SK_FixedHalf;
146        y -= SK_FixedHalf;
147
148        // compute our pointers
149        {
150            const SkBitmap* bitmap = &fBitmap;
151            int ix = x >> 16;
152            int iy = y >> 16;
153
154            int             maxX = fMaxX;
155            SkTileModeProc  procX = fTileProcX;
156            int             maxY = fMaxY;
157            SkTileModeProc  procY = fTileProcY;
158
159            int tmpx = procX(ix, maxX);
160            int tmpy = procY(iy, maxY);
161            p00 = bitmap->getAddr16(tmpx, tmpy);
162
163            int tmpx1 = procX(ix + 1, maxX);
164            p01 = bitmap->getAddr16(tmpx1, tmpy);
165
166            int tmpy1 = procY(iy + 1, maxY);
167            p10 = bitmap->getAddr16(tmpx, tmpy1);
168
169            p11 = bitmap->getAddr16(tmpx1, tmpy1);
170        }
171
172        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
173        uint32_t c = proc(SkExpand_rgb_16(*p00), SkExpand_rgb_16(*p01),
174                          SkExpand_rgb_16(*p10), SkExpand_rgb_16(*p11));
175
176        return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c));
177    }
178
179private:
180    const SkFilterProc* fProcTable;
181};
182
183// If we had a init/term method on sampler, we could avoid the per-pixel
184// call to lockColors/unlockColors
185
186class Index8_Bilinear_Sampler : public SkBitmapSampler {
187public:
188    Index8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
189        : SkBitmapSampler(bm, true, tmx, tmy)
190    {
191        fPtrProcTable = SkGetBilinearFilterPtrProcTable();
192    }
193
194    virtual SkPMColor sample(SkFixed x, SkFixed y) const
195    {
196        const SkBitmap* bitmap = &fBitmap;
197
198        const uint8_t *p00, *p01, *p10, *p11;
199
200         // turn pixel centers into the top-left of our filter-box
201        x -= SK_FixedHalf;
202        y -= SK_FixedHalf;
203
204       // compute our pointers
205        {
206            int ix = x >> 16;
207            int iy = y >> 16;
208
209            int             maxX = fMaxX;
210            SkTileModeProc  procX = fTileProcX;
211            int             maxY = fMaxY;
212            SkTileModeProc  procY = fTileProcY;
213
214            int tmpx = procX(ix, maxX);
215            int tmpy = procY(iy, maxY);
216            p00 = bitmap->getAddr8(tmpx, tmpy);
217
218            int tmpx1 = procX(ix + 1, maxX);
219            p01 = bitmap->getAddr8(tmpx1, tmpy);
220
221            int tmpy1 = procY(iy + 1, maxY);
222            p10 = bitmap->getAddr8(tmpx, tmpy1);
223
224            p11 = bitmap->getAddr8(tmpx1, tmpy1);
225        }
226
227        const SkPMColor* colors = bitmap->getColorTable()->lockColors();
228
229        SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
230        uint32_t c = proc(&colors[*p00], &colors[*p01], &colors[*p10], &colors[*p11]);
231
232        bitmap->getColorTable()->unlockColors(false);
233
234        return c;
235    }
236
237private:
238    const SkFilterPtrProc* fPtrProcTable;
239};
240
241class A8_Bilinear_Sampler : public SkBitmapSampler {
242public:
243    A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
244        : SkBitmapSampler(bm, true, tmx, tmy)
245    {
246        fProcTable = SkGetBilinearFilterProcTable();
247    }
248
249    virtual void setPaint(const SkPaint& paint)
250    {
251        fColor = SkPreMultiplyColor(paint.getColor());
252    }
253
254    virtual SkPMColor sample(SkFixed x, SkFixed y) const
255    {
256        const uint8_t *p00, *p01, *p10, *p11;
257
258        // turn pixel centers into the top-left of our filter-box
259        x -= SK_FixedHalf;
260        y -= SK_FixedHalf;
261
262        // compute our pointers
263        {
264            const SkBitmap* bitmap = &fBitmap;
265            int ix = x >> 16;
266            int iy = y >> 16;
267
268            int             maxX = fMaxX;
269            SkTileModeProc  procX = fTileProcX;
270            int             maxY = fMaxY;
271            SkTileModeProc  procY = fTileProcY;
272
273            int tmpx = procX(ix, maxX);
274            int tmpy = procY(iy, maxY);
275            p00 = bitmap->getAddr8(tmpx, tmpy);
276
277            int tmpx1 = procX(ix + 1, maxX);
278            p01 = bitmap->getAddr8(tmpx1, tmpy);
279
280            int tmpy1 = procY(iy + 1, maxY);
281            p10 = bitmap->getAddr8(tmpx, tmpy1);
282
283            p11 = bitmap->getAddr8(tmpx1, tmpy1);
284        }
285
286        SkFilterProc proc = SkGetBilinearFilterProc(fProcTable, x, y);
287        int alpha = proc(*p00, *p01, *p10, *p11);
288        return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
289    }
290
291private:
292    const SkFilterProc* fProcTable;
293    SkPMColor           fColor;
294};
295
296class A8_NoFilter_Sampler : public SkBitmapSampler {
297public:
298    A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
299        : SkBitmapSampler(bm, false, tmx, tmy)
300    {
301    }
302
303    virtual void setPaint(const SkPaint& paint)
304    {
305        fColor = SkPreMultiplyColor(paint.getColor());
306    }
307
308    virtual SkPMColor sample(SkFixed x, SkFixed y) const
309    {
310        int ix = SkFixedFloor(x);
311        int iy = SkFixedFloor(y);
312
313        int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY));
314        return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
315    }
316
317private:
318    const SkFilterProc* fProcTable;
319    SkPMColor           fColor;
320};
321
322///////////////////////////////////////////////////////////////////////////////
323///////////////////////////////////////////////////////////////////////////////
324
325SkBitmapSampler* SkBitmapSampler::Create(const SkBitmap& bm, bool doFilter,
326                                         SkShader::TileMode tmx,
327                                         SkShader::TileMode tmy)
328{
329    switch (bm.getConfig()) {
330    case SkBitmap::kARGB_8888_Config:
331        if (doFilter)
332            return SkNEW_ARGS(ARGB32_Bilinear_Sampler, (bm, tmx, tmy));
333
334        if (tmx == tmy) {
335            switch (tmx) {
336            case SkShader::kClamp_TileMode:
337                return SkNEW_ARGS(ARGB32_Point_Clamp_Sampler, (bm));
338            case SkShader::kRepeat_TileMode:
339                if (is_pow2(bm.width()) && is_pow2(bm.height()))
340                    return SkNEW_ARGS(ARGB32_Point_Repeat_Pow2_Sampler, (bm));
341                else
342                    return SkNEW_ARGS(ARGB32_Point_Repeat_Mod_Sampler, (bm));
343            case SkShader::kMirror_TileMode:
344                if (is_pow2(bm.width()) && is_pow2(bm.height()))
345                    return SkNEW_ARGS(ARGB32_Point_Mirror_Pow2_Sampler, (bm));
346                else
347                    return SkNEW_ARGS(ARGB32_Point_Mirror_Mod_Sampler, (bm));
348            default:
349                SkASSERT(!"unknown mode");
350            }
351        }
352        else {  // tmx != tmy
353            return SkNEW_ARGS(ARGB32_Point_Sampler, (bm, tmx, tmy));
354        }
355        break;
356
357    case SkBitmap::kRGB_565_Config:
358        if (doFilter)
359            return SkNEW_ARGS(RGB16_Bilinear_Sampler, (bm, tmx, tmy));
360
361        if (tmx == tmy) {
362            switch (tmx) {
363            case SkShader::kClamp_TileMode:
364                return SkNEW_ARGS(RGB16_Point_Clamp_Sampler, (bm));
365            case SkShader::kRepeat_TileMode:
366                if (is_pow2(bm.width()) && is_pow2(bm.height()))
367                    return SkNEW_ARGS(RGB16_Point_Repeat_Pow2_Sampler, (bm));
368                else
369                    return SkNEW_ARGS(RGB16_Point_Repeat_Mod_Sampler, (bm));
370            case SkShader::kMirror_TileMode:
371                if (is_pow2(bm.width()) && is_pow2(bm.height()))
372                    return SkNEW_ARGS(RGB16_Point_Mirror_Pow2_Sampler, (bm));
373                else
374                    return SkNEW_ARGS(RGB16_Point_Mirror_Mod_Sampler, (bm));
375            default:
376                SkASSERT(!"unknown mode");
377            }
378        }
379        else {  // tmx != tmy
380            return SkNEW_ARGS(RGB16_Point_Sampler, (bm, tmx, tmy));
381        }
382        break;
383
384    case SkBitmap::kIndex8_Config:
385        if (doFilter)
386            return SkNEW_ARGS(Index8_Bilinear_Sampler, (bm, tmx, tmy));
387
388        if (tmx == tmy) {
389            switch (tmx) {
390            case SkShader::kClamp_TileMode:
391                return SkNEW_ARGS(Index8_Point_Clamp_Sampler, (bm));
392            case SkShader::kRepeat_TileMode:
393                if (is_pow2(bm.width()) && is_pow2(bm.height()))
394                    return SkNEW_ARGS(Index8_Point_Repeat_Pow2_Sampler, (bm));
395                else
396                    return SkNEW_ARGS(Index8_Point_Repeat_Mod_Sampler, (bm));
397            case SkShader::kMirror_TileMode:
398                if (is_pow2(bm.width()) && is_pow2(bm.height()))
399                    return SkNEW_ARGS(Index8_Point_Mirror_Pow2_Sampler, (bm));
400                else
401                    return SkNEW_ARGS(Index8_Point_Mirror_Mod_Sampler, (bm));
402            default:
403                SkASSERT(!"unknown mode");
404            }
405        }
406        else {  // tmx != tmy
407            return SkNEW_ARGS(Index8_Point_Sampler, (bm, tmx, tmy));
408        }
409        break;
410
411    case SkBitmap::kA8_Config:
412        if (doFilter)
413            return SkNEW_ARGS(A8_Bilinear_Sampler, (bm, tmx, tmy));
414        else
415            return SkNEW_ARGS(A8_NoFilter_Sampler, (bm, tmx, tmy));
416        break;
417
418    default:
419        SkASSERT(!"unknown device");
420    }
421    return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
422}
423
424