blob: e1054932620dd3373f717c81a47203908781c8a3 [file] [log] [blame]
reed@google.comdc6c8ba2013-07-18 21:14:04 +00001/*
2 * Copyright 2013 Google Inc.
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
reed@google.comeed6f1b2013-07-18 19:53:31 +00008#include "SkMipMap.h"
9#include "SkBitmap.h"
10#include "SkColorPriv.h"
mtklein550e9b02016-01-20 11:55:51 -080011#include "SkNx.h"
reed@google.comeed6f1b2013-07-18 19:53:31 +000012
reed32e0b4a2016-01-15 13:17:08 -080013//
14// ColorTypeFilter is the "Type" we pass to some downsample template functions.
15// It controls how we expand a pixel into a large type, with space between each component,
16// so we can then perform our simple filter (either box or triangle) and store the intermediates
17// in the expanded type.
18//
19
20struct ColorTypeFilter_8888 {
21 typedef uint32_t Type;
mtklein550e9b02016-01-20 11:55:51 -080022#if defined(SKNX_IS_FAST)
23 static Sk4h Expand(uint32_t x) {
24 return SkNx_cast<uint16_t>(Sk4b::Load((const uint8_t*)&x));
25 }
26 static uint32_t Compact(const Sk4h& x) {
27 uint32_t r;
28 SkNx_cast<uint8_t>(x).store((uint8_t*)&r);
29 return r;
30 }
31#else
reed32e0b4a2016-01-15 13:17:08 -080032 static uint64_t Expand(uint32_t x) {
33 return (x & 0xFF00FF) | ((uint64_t)(x & 0xFF00FF00) << 24);
34 }
35 static uint32_t Compact(uint64_t x) {
36 return (uint32_t)((x & 0xFF00FF) | ((x >> 24) & 0xFF00FF00));
37 }
mtklein550e9b02016-01-20 11:55:51 -080038#endif
reed32e0b4a2016-01-15 13:17:08 -080039};
40
41struct ColorTypeFilter_565 {
42 typedef uint16_t Type;
43 static uint32_t Expand(uint16_t x) {
44 return (x & ~SK_G16_MASK_IN_PLACE) | ((x & SK_G16_MASK_IN_PLACE) << 16);
45 }
46 static uint16_t Compact(uint32_t x) {
47 return (x & ~SK_G16_MASK_IN_PLACE) | ((x >> 16) & SK_G16_MASK_IN_PLACE);
48 }
49};
50
51struct ColorTypeFilter_4444 {
52 typedef uint16_t Type;
53 static uint32_t Expand(uint16_t x) {
54 return (x & 0xF0F) | ((x & ~0xF0F) << 12);
55 }
56 static uint16_t Compact(uint32_t x) {
57 return (x & 0xF0F) | ((x >> 12) & ~0xF0F);
58 }
59};
60
61struct ColorTypeFilter_8 {
62 typedef uint8_t Type;
63 static unsigned Expand(unsigned x) {
64 return x;
65 }
66 static uint8_t Compact(unsigned x) {
67 return (uint8_t)x;
68 }
69};
70
mtklein550e9b02016-01-20 11:55:51 -080071template <typename T> T add_121(const T& a, const T& b, const T& c) {
reed32e0b4a2016-01-15 13:17:08 -080072 return a + b + b + c;
73}
74
75//
76// To produce each mip level, we need to filter down by 1/2 (e.g. 100x100 -> 50,50)
77// If the starting dimension is odd, we floor the size of the lower level (e.g. 101 -> 50)
78// In those (odd) cases, we use a triangle filter, with 1-pixel overlap between samplings,
79// else for even cases, we just use a 2x box filter.
80//
81// This produces 4 possible filters: 2x2 2x3 3x2 3x3 where WxH indicates the number of src pixels
82// we need to sample in each dimension to produce 1 dst pixel.
83//
84
reed326253e2016-01-16 09:23:48 -080085template <typename F> void downsample_2_2(void* dst, const void* src, size_t srcRB, int count) {
reed32e0b4a2016-01-15 13:17:08 -080086 auto p0 = static_cast<const typename F::Type*>(src);
87 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
reed326253e2016-01-16 09:23:48 -080088 auto d = static_cast<typename F::Type*>(dst);
reed32e0b4a2016-01-15 13:17:08 -080089
reed326253e2016-01-16 09:23:48 -080090 for (int i = 0; i < count; ++i) {
91 auto c00 = F::Expand(p0[0]);
92 auto c01 = F::Expand(p0[1]);
93 auto c10 = F::Expand(p1[0]);
94 auto c11 = F::Expand(p1[1]);
reed32e0b4a2016-01-15 13:17:08 -080095
reed326253e2016-01-16 09:23:48 -080096 auto c = c00 + c10 + c01 + c11;
97 d[i] = F::Compact(c >> 2);
98 p0 += 2;
99 p1 += 2;
100 }
reed32e0b4a2016-01-15 13:17:08 -0800101}
102
reed326253e2016-01-16 09:23:48 -0800103template <typename F> void downsample_3_2(void* dst, const void* src, size_t srcRB, int count) {
104 SkASSERT(count > 0);
reed32e0b4a2016-01-15 13:17:08 -0800105 auto p0 = static_cast<const typename F::Type*>(src);
106 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
reed326253e2016-01-16 09:23:48 -0800107 auto d = static_cast<typename F::Type*>(dst);
mtklein550e9b02016-01-20 11:55:51 -0800108
reed326253e2016-01-16 09:23:48 -0800109 auto c02 = F::Expand(p0[0]);
110 auto c12 = F::Expand(p1[0]);
111 for (int i = 0; i < count; ++i) {
112 auto c00 = c02;
113 auto c01 = F::Expand(p0[1]);
114 c02 = F::Expand(p0[2]);
115 auto c10 = c12;
116 auto c11 = F::Expand(p1[1]);
117 c12 = F::Expand(p1[2]);
reed32e0b4a2016-01-15 13:17:08 -0800118
reed326253e2016-01-16 09:23:48 -0800119 auto c = add_121(c00, c01, c02) + add_121(c10, c11, c12);
120 d[i] = F::Compact(c >> 3);
121 p0 += 2;
122 p1 += 2;
123 }
reed32e0b4a2016-01-15 13:17:08 -0800124}
125
reed326253e2016-01-16 09:23:48 -0800126template <typename F> void downsample_2_3(void* dst, const void* src, size_t srcRB, int count) {
reed32e0b4a2016-01-15 13:17:08 -0800127 auto p0 = static_cast<const typename F::Type*>(src);
128 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
129 auto p2 = (const typename F::Type*)((const char*)p1 + srcRB);
reed326253e2016-01-16 09:23:48 -0800130 auto d = static_cast<typename F::Type*>(dst);
mtklein550e9b02016-01-20 11:55:51 -0800131
reed326253e2016-01-16 09:23:48 -0800132 for (int i = 0; i < count; ++i) {
133 auto c00 = F::Expand(p0[0]);
134 auto c01 = F::Expand(p0[1]);
135 auto c10 = F::Expand(p1[0]);
136 auto c11 = F::Expand(p1[1]);
137 auto c20 = F::Expand(p2[0]);
138 auto c21 = F::Expand(p2[1]);
reed32e0b4a2016-01-15 13:17:08 -0800139
reed326253e2016-01-16 09:23:48 -0800140 auto c = add_121(c00, c10, c20) + add_121(c01, c11, c21);
141 d[i] = F::Compact(c >> 3);
142 p0 += 2;
143 p1 += 2;
144 p2 += 2;
145 }
reed32e0b4a2016-01-15 13:17:08 -0800146}
147
reed326253e2016-01-16 09:23:48 -0800148template <typename F> void downsample_3_3(void* dst, const void* src, size_t srcRB, int count) {
reed32e0b4a2016-01-15 13:17:08 -0800149 auto p0 = static_cast<const typename F::Type*>(src);
150 auto p1 = (const typename F::Type*)((const char*)p0 + srcRB);
151 auto p2 = (const typename F::Type*)((const char*)p1 + srcRB);
reed326253e2016-01-16 09:23:48 -0800152 auto d = static_cast<typename F::Type*>(dst);
reed32e0b4a2016-01-15 13:17:08 -0800153
reed326253e2016-01-16 09:23:48 -0800154 auto c02 = F::Expand(p0[0]);
155 auto c12 = F::Expand(p1[0]);
156 auto c22 = F::Expand(p2[0]);
157 for (int i = 0; i < count; ++i) {
158 auto c00 = c02;
159 auto c01 = F::Expand(p0[1]);
160 c02 = F::Expand(p0[2]);
161 auto c10 = c12;
162 auto c11 = F::Expand(p1[1]);
163 c12 = F::Expand(p1[2]);
164 auto c20 = c22;
165 auto c21 = F::Expand(p2[1]);
166 c22 = F::Expand(p2[2]);
reed32e0b4a2016-01-15 13:17:08 -0800167
reed326253e2016-01-16 09:23:48 -0800168 auto c = add_121(c00, c01, c02) + (add_121(c10, c11, c12) << 1) + add_121(c20, c21, c22);
169 d[i] = F::Compact(c >> 4);
170 p0 += 2;
171 p1 += 2;
172 p2 += 2;
173 }
reed32e0b4a2016-01-15 13:17:08 -0800174}
175
176///////////////////////////////////////////////////////////////////////////////////////////////////
177
178size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) {
179 if (levelCount < 0) {
180 return 0;
181 }
182 int64_t size = sk_64_mul(levelCount + 1, sizeof(Level)) + pixelSize;
183 if (!sk_64_isS32(size)) {
184 return 0;
185 }
186 return sk_64_asS32(size);
187}
188
reed67b09bf2016-01-16 18:50:35 -0800189SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
reed326253e2016-01-16 09:23:48 -0800190 typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count);
reed32e0b4a2016-01-15 13:17:08 -0800191
192 FilterProc* proc_2_2 = nullptr;
193 FilterProc* proc_2_3 = nullptr;
194 FilterProc* proc_3_2 = nullptr;
195 FilterProc* proc_3_3 = nullptr;
196
197 const SkColorType ct = src.colorType();
198 const SkAlphaType at = src.alphaType();
199 switch (ct) {
200 case kRGBA_8888_SkColorType:
201 case kBGRA_8888_SkColorType:
202 proc_2_2 = downsample_2_2<ColorTypeFilter_8888>;
203 proc_2_3 = downsample_2_3<ColorTypeFilter_8888>;
204 proc_3_2 = downsample_3_2<ColorTypeFilter_8888>;
205 proc_3_3 = downsample_3_3<ColorTypeFilter_8888>;
206 break;
207 case kRGB_565_SkColorType:
208 proc_2_2 = downsample_2_2<ColorTypeFilter_565>;
209 proc_2_3 = downsample_2_3<ColorTypeFilter_565>;
210 proc_3_2 = downsample_3_2<ColorTypeFilter_565>;
211 proc_3_3 = downsample_3_3<ColorTypeFilter_565>;
212 break;
213 case kARGB_4444_SkColorType:
214 proc_2_2 = downsample_2_2<ColorTypeFilter_4444>;
215 proc_2_3 = downsample_2_3<ColorTypeFilter_4444>;
216 proc_3_2 = downsample_3_2<ColorTypeFilter_4444>;
217 proc_3_3 = downsample_3_3<ColorTypeFilter_4444>;
218 break;
219 case kAlpha_8_SkColorType:
220 case kGray_8_SkColorType:
221 proc_2_2 = downsample_2_2<ColorTypeFilter_8>;
222 proc_2_3 = downsample_2_3<ColorTypeFilter_8>;
223 proc_3_2 = downsample_3_2<ColorTypeFilter_8>;
224 proc_3_3 = downsample_3_3<ColorTypeFilter_8>;
225 break;
226 default:
227 // TODO: We could build miplevels for kIndex8 if the levels were in 8888.
228 // Means using more ram, but the quality would be fine.
229 return nullptr;
230 }
231
232 // whip through our loop to compute the exact size needed
233 size_t size = 0;
234 int countLevels = 0;
235 {
236 int width = src.width();
237 int height = src.height();
238 for (;;) {
239 width >>= 1;
240 height >>= 1;
241 if (0 == width || 0 == height) {
242 break;
243 }
244 size += SkColorTypeMinRowBytes(ct, width) * height;
245 countLevels += 1;
246 }
247 }
248 if (0 == countLevels) {
249 return nullptr;
250 }
251
252 size_t storageSize = SkMipMap::AllocLevelsSize(countLevels, size);
253 if (0 == storageSize) {
254 return nullptr;
255 }
256
reed32e0b4a2016-01-15 13:17:08 -0800257 SkMipMap* mipmap;
258 if (fact) {
259 SkDiscardableMemory* dm = fact(storageSize);
260 if (nullptr == dm) {
261 return nullptr;
262 }
263 mipmap = new SkMipMap(storageSize, dm);
264 } else {
265 mipmap = new SkMipMap(sk_malloc_throw(storageSize), storageSize);
266 }
267
268 // init
269 mipmap->fCount = countLevels;
270 mipmap->fLevels = (Level*)mipmap->writable_data();
271
272 Level* levels = mipmap->fLevels;
273 uint8_t* baseAddr = (uint8_t*)&levels[countLevels];
274 uint8_t* addr = baseAddr;
275 int width = src.width();
276 int height = src.height();
277 uint32_t rowBytes;
reed67b09bf2016-01-16 18:50:35 -0800278 SkPixmap srcPM(src);
reed32e0b4a2016-01-15 13:17:08 -0800279
reed32e0b4a2016-01-15 13:17:08 -0800280 for (int i = 0; i < countLevels; ++i) {
reed326253e2016-01-16 09:23:48 -0800281 FilterProc* proc;
282 if (height & 1) { // src-height is 3
283 if (width & 1) { // src-width is 3
284 proc = proc_3_3;
285 } else { // src-width is 2
286 proc = proc_2_3;
287 }
288 } else { // src-height is 2
289 if (width & 1) { // src-width is 3
290 proc = proc_3_2;
291 } else { // src-width is 2
292 proc = proc_2_2;
293 }
294 }
reed32e0b4a2016-01-15 13:17:08 -0800295 width >>= 1;
296 height >>= 1;
297 rowBytes = SkToU32(SkColorTypeMinRowBytes(ct, width));
298
reed67b09bf2016-01-16 18:50:35 -0800299 levels[i].fPixmap = SkPixmap(SkImageInfo::Make(width, height, ct, at), addr, rowBytes);
fmalita921d7ac2016-01-22 11:45:39 -0800300 levels[i].fScale = SkSize::Make(SkIntToScalar(width) / src.width(),
301 SkIntToScalar(height) / src.height());
reed32e0b4a2016-01-15 13:17:08 -0800302
reed67b09bf2016-01-16 18:50:35 -0800303 const SkPixmap& dstPM = levels[i].fPixmap;
reed32e0b4a2016-01-15 13:17:08 -0800304 const void* srcBasePtr = srcPM.addr();
305 void* dstBasePtr = dstPM.writable_addr();
306
reed32e0b4a2016-01-15 13:17:08 -0800307 const size_t srcRB = srcPM.rowBytes();
308 for (int y = 0; y < height; y++) {
reed326253e2016-01-16 09:23:48 -0800309 proc(dstBasePtr, srcBasePtr, srcRB, width);
reed32e0b4a2016-01-15 13:17:08 -0800310 srcBasePtr = (char*)srcBasePtr + srcRB * 2; // jump two rows
311 dstBasePtr = (char*)dstBasePtr + dstPM.rowBytes();
312 }
313 srcPM = dstPM;
314 addr += height * rowBytes;
reed32e0b4a2016-01-15 13:17:08 -0800315 }
316 SkASSERT(addr == baseAddr + size);
reed@google.comeed6f1b2013-07-18 19:53:31 +0000317
reed9d93c2e2014-10-08 05:17:12 -0700318 return mipmap;
reed@google.comd94697c2013-07-24 14:31:33 +0000319}
320
321///////////////////////////////////////////////////////////////////////////////
322
reed@google.comeed6f1b2013-07-18 19:53:31 +0000323bool SkMipMap::extractLevel(SkScalar scale, Level* levelPtr) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700324 if (nullptr == fLevels) {
reed9d93c2e2014-10-08 05:17:12 -0700325 return false;
326 }
327
reed7729e562015-01-16 08:35:09 -0800328 if (scale >= SK_Scalar1 || scale <= 0 || !SkScalarIsFinite(scale)) {
reed@google.comeed6f1b2013-07-18 19:53:31 +0000329 return false;
330 }
331
reed7729e562015-01-16 08:35:09 -0800332 SkScalar L = -SkScalarLog2(scale);
333 if (!SkScalarIsFinite(L)) {
334 return false;
335 }
336 SkASSERT(L >= 0);
reedc1e1faa2015-05-06 13:02:37 -0700337// int rndLevel = SkScalarRoundToInt(L);
338 int level = SkScalarFloorToInt(L);
reedc1e1faa2015-05-06 13:02:37 -0700339// SkDebugf("mipmap scale=%g L=%g level=%d rndLevel=%d\n", scale, L, level, rndLevel);
reed7729e562015-01-16 08:35:09 -0800340
reed@google.comeed6f1b2013-07-18 19:53:31 +0000341 SkASSERT(level >= 0);
342 if (level <= 0) {
343 return false;
344 }
345
346 if (level > fCount) {
347 level = fCount;
348 }
349 if (levelPtr) {
350 *levelPtr = fLevels[level - 1];
351 }
352 return true;
353}
reed67b09bf2016-01-16 18:50:35 -0800354
cblume61214052016-01-26 09:10:48 -0800355// Helper which extracts a pixmap from the src bitmap
reed67b09bf2016-01-16 18:50:35 -0800356//
357SkMipMap* SkMipMap::Build(const SkBitmap& src, SkDiscardableFactoryProc fact) {
358 SkAutoPixmapUnlock srcUnlocker;
359 if (!src.requestLock(&srcUnlocker)) {
360 return nullptr;
361 }
362 const SkPixmap& srcPixmap = srcUnlocker.pixmap();
363 // Try to catch where we might have returned nullptr for src crbug.com/492818
364 if (nullptr == srcPixmap.addr()) {
365 sk_throw();
366 }
367 return Build(srcPixmap, fact);
368}
369