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