1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/* 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file. 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */ 8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkEmbossMask.h" 11889bd8bd7f604acae0a6303365bc82c06da1e6f3tomhudson@google.com#include "SkMath.h" 128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 133334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic inline int nonzero_to_one(int x) { 148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 0 158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return x != 0; 168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#else 178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return ((unsigned)(x | -x)) >> 31; 188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 213334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic inline int neq_to_one(int x, int max) { 228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 0 238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return x != max; 248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#else 258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(x >= 0 && x <= max); 268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return ((unsigned)(x - max)) >> 31; 278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 303334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic inline int neq_to_mask(int x, int max) { 318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 0 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return -(x != max); 338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#else 348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(x >= 0 && x <= max); 358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return (x - max) >> 31; 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 393334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic inline unsigned div255(unsigned x) { 408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(x <= (255*255)); 418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com return x * ((1 << 24) / 255) >> 24; 428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#define kDelta 32 // small enough to show off angle differences 458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkEmbossMask_Table.h" 478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG) 498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include <stdio.h> 518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 523334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgvoid SkEmbossMask_BuildTable() { 538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table 548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com FILE* file = ::fopen("SkEmbossMask_Table.h", "w"); 568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(file); 578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "#include \"SkTypes.h\"\n\n"); 588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "static const U16 gInvSqrtTable[128 * 128] = {\n"); 593334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org for (int dx = 0; dx <= 255/2; dx++) { 603334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org for (int dy = 0; dy <= 255/2; dy++) { 618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com if ((dy & 15) == 0) 628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "\t"); 638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 649c970453fd1cb6e9618e37c61507465772deca80reed@android.com uint16_t value = SkToU16((1 << 15) / SkSqrt32(dx * dx + dy * dy + kDelta*kDelta/4)); 658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "0x%04X", value); 673334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org if (dx * 128 + dy < 128*128-1) { 688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, ", "); 693334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org } 703334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org if ((dy & 15) == 15) { 718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "\n"); 723334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org } 738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fprintf(file, "};\n#define kDeltaUsedToBuildTable\t%d\n", kDelta); 768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com ::fclose(file); 778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 813334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgvoid SkEmbossMask::Emboss(SkMask* mask, const SkEmbossMaskFilter::Light& light) { 828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(kDelta == kDeltaUsedToBuildTable); 838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkASSERT(mask->fFormat == SkMask::k3D_Format); 858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int specular = light.fSpecular; 878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int ambient = light.fAmbient; 888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed lx = SkScalarToFixed(light.fDirection[0]); 898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed ly = SkScalarToFixed(light.fDirection[1]); 908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed lz = SkScalarToFixed(light.fDirection[2]); 918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed lz_dot_nz = lz * kDelta; 928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int lz_dot8 = lz >> 8; 938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com size_t planeSize = mask->computeImageSize(); 958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com uint8_t* alpha = mask->fImage; 968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com uint8_t* multiply = (uint8_t*)alpha + planeSize; 978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com uint8_t* additive = multiply + planeSize; 988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int rowBytes = mask->fRowBytes; 1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int maxy = mask->fBounds.height() - 1; 1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int maxx = mask->fBounds.width() - 1; 1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int prev_row = 0; 1043334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org for (int y = 0; y <= maxy; y++) { 1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int next_row = neq_to_mask(y, maxy) & rowBytes; 1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1073334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org for (int x = 0; x <= maxx; x++) { 1083334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org if (alpha[x]) { 1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int nx = alpha[x + neq_to_one(x, maxx)] - alpha[x - nonzero_to_one(x)]; 1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int ny = alpha[x + next_row] - alpha[x - prev_row]; 1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed numer = lx * nx + ly * ny + lz_dot_nz; 1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int mul = ambient; 1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int add = 0; 1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1163334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org if (numer > 0) { // preflight when numer/denom will be <= 0 1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 0 1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int denom = SkSqrt32(nx * nx + ny * ny + kDelta*kDelta); 1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed dot = numer / denom; 1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com dot >>= 8; // now dot is 2^8 instead of 2^16 1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#else 1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // can use full numer, but then we need to call SkFixedMul, since 1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // numer is 24 bits, and our table is 12 bits 1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // SkFixed dot = SkFixedMul(numer, gTable[]) >> 8 1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com SkFixed dot = (unsigned)(numer >> 4) * gInvSqrtTable[(SkAbs32(nx) >> 1 << 7) | (SkAbs32(ny) >> 1)] >> 20; 1278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif 1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com mul = SkFastMin32(mul + dot, 255); 1298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // now for the reflection 1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // R = 2 (Light * Normal) Normal - Light 1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // hilite = R * Eye(0, 0, 1) 1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com int hilite = (2 * dot - lz_dot8) * lz_dot8 >> 8; 1363334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org if (hilite > 0) { 1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // pin hilite to 255, since our fast math is also a little sloppy 1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com hilite = SkClampMax(hilite, 255); 1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // specular is 4.4 1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // would really like to compute the fractional part of this 1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // and then possibly cache a 256 table for a given specular 1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // value in the light, and just pass that in to this function. 1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com add = hilite; 1453334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org for (int i = specular >> 4; i > 0; --i) { 1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com add = div255(add * hilite); 1473334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org } 1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com multiply[x] = SkToU8(mul); 1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com additive[x] = SkToU8(add); 1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com 1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // multiply[x] = 0xFF; 1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // additive[x] = 0; 1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com // ((uint8_t*)alpha)[x] = alpha[x] * multiply[x] >> 8; 1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com alpha += rowBytes; 1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com multiply += rowBytes; 1608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com additive += rowBytes; 1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com prev_row = rowBytes; 1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com } 1638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com} 164