Example #1
0
    void generateAAFillRectGeometry(void* vertices,
                                    size_t offset,
                                    size_t vertexStride,
                                    GrColor color,
                                    const SkMatrix& viewMatrix,
                                    const SkRect& rect,
                                    const SkRect& devRect,
                                    bool tweakAlphaForCoverage) const {
        intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;

        SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
        SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);

        SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
        inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());

        if (viewMatrix.rectStaysRect()) {
            set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
            set_inset_fan(fan1Pos, vertexStride, devRect, inset,  inset);
        } else {
            // compute transformed (1, 0) and (0, 1) vectors
            SkVector vec[2] = {
              { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
              { viewMatrix[SkMatrix::kMSkewX],  viewMatrix[SkMatrix::kMScaleY] }
            };

            vec[0].normalize();
            vec[0].scale(SK_ScalarHalf);
            vec[1].normalize();
            vec[1].scale(SK_ScalarHalf);

            // create the rotated rect
            fan0Pos->setRectFan(rect.fLeft, rect.fTop,
                                rect.fRight, rect.fBottom, vertexStride);
            viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);

            // Now create the inset points and then outset the original
            // rotated points

            // TL
            *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
            *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
            // BL
            *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
            *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
            // BR
            *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
            *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
            // TR
            *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
            *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
        }

        // Make verts point to vertex color and then set all the color and coverage vertex attrs
        // values.
        verts += sizeof(SkPoint);
        for (int i = 0; i < 4; ++i) {
            if (tweakAlphaForCoverage) {
                *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
            } else {
                *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
                *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
            }
        }

        int scale;
        if (inset < SK_ScalarHalf) {
            scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
            SkASSERT(scale >= 0 && scale <= 255);
        } else {
            scale = 0xff;
        }

        verts += 4 * vertexStride;

        float innerCoverage = GrNormalizeByteToFloat(scale);
        GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);

        for (int i = 0; i < 4; ++i) {
            if (tweakAlphaForCoverage) {
                *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
            } else {
                *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
                *reinterpret_cast<float*>(verts + i * vertexStride +
                                          sizeof(GrColor)) = innerCoverage;
            }
        }
    }
bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
                                  SkPoint dst[2]) {
    SkRect bounds;

    bounds.set(src, 2);
    if (containsNoEmptyCheck(clip, bounds)) {
        if (src != dst) {
            memcpy(dst, src, 2 * sizeof(SkPoint));
        }
        return true;
    }
    // check for no overlap, and only permit coincident edges if the line
    // and the edge are colinear
    if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) ||
        nestedLT(clip.fRight, bounds.fLeft, bounds.width()) ||
        nestedLT(bounds.fBottom, clip.fTop, bounds.height()) ||
        nestedLT(clip.fBottom, bounds.fTop, bounds.height())) {
        return false;
    }

    int index0, index1;

    if (src[0].fY < src[1].fY) {
        index0 = 0;
        index1 = 1;
    } else {
        index0 = 1;
        index1 = 0;
    }

    SkPoint tmp[2];
    memcpy(tmp, src, sizeof(tmp));

    // now compute Y intersections
    if (tmp[index0].fY < clip.fTop) {
        tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop);
    }
    if (tmp[index1].fY > clip.fBottom) {
        tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom);
    }

    if (tmp[0].fX < tmp[1].fX) {
        index0 = 0;
        index1 = 1;
    } else {
        index0 = 1;
        index1 = 0;
    }

    // check for quick-reject in X again, now that we may have been chopped
    if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight) &&
        tmp[index0].fX < tmp[index1].fX) {
        // only reject if we have a non-zero width
        return false;
    }

    if (tmp[index0].fX < clip.fLeft) {
        tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft));
    }
    if (tmp[index1].fX > clip.fRight) {
        tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
    }
#ifdef SK_DEBUG
    bounds.set(tmp, 2);
    SkASSERT(containsNoEmptyCheck(clip, bounds));
#endif
    memcpy(dst, tmp, sizeof(tmp));
    return true;
}
Example #3
0
static SkRect inset(const SkRect& r) {
    SkRect rect = r;
    rect.inset(r.width() / 8, r.height() / 8);
    return rect;
}
Example #4
0
    virtual void onDraw(SkCanvas* canvas) {
        struct FillAndName {
            SkPath::FillType fFill;
            const char*      fName;
        };
        static const FillAndName gFills[] = {
            {SkPath::kWinding_FillType, "Winding"},
            {SkPath::kEvenOdd_FillType, "Even / Odd"},
            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
        };
        struct StyleAndName {
            SkPaint::Style fStyle;
            const char*    fName;
        };
        static const StyleAndName gStyles[] = {
            {SkPaint::kFill_Style, "Fill"},
            {SkPaint::kStroke_Style, "Stroke"},
            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
        };
        struct CapAndName {
            SkPaint::Cap  fCap;
            SkPaint::Join fJoin;
            const char*   fName;
        };
        static const CapAndName gCaps[] = {
            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
        };
        struct PathAndName {
            SkPath      fPath;
            const char* fName;
        };
        PathAndName path;
        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
                           60*SK_Scalar1, 20*SK_Scalar1,
                           75*SK_Scalar1, 10*SK_Scalar1);
        path.fPath.close();
        path.fName = "moveTo-cubic-close";

        SkPaint titlePaint;
        titlePaint.setColor(SK_ColorBLACK);
        titlePaint.setAntiAlias(true);
        sk_tool_utils::set_portable_typeface(&titlePaint);
        titlePaint.setTextSize(15 * SK_Scalar1);
        const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
                             "Indicated Style, Fill and Linecaps, with stroke width 10";
        canvas->drawText(title, strlen(title),
                            20 * SK_Scalar1,
                            20 * SK_Scalar1,
                            titlePaint);

        SkRandom rand;
        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
        canvas->save();
        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
        canvas->save();
        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
            if (0 < cap) {
                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
            }
            canvas->save();
            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                if (0 < fill) {
                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
                }
                canvas->save();
                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
                    if (0 < style) {
                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                    }

                    SkColor color = 0xff007000;
                    this->drawPath(path.fPath, canvas, color, rect,
                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                    gFills[fill].fFill, SK_Scalar1*10);

                    SkPaint rectPaint;
                    rectPaint.setColor(SK_ColorBLACK);
                    rectPaint.setStyle(SkPaint::kStroke_Style);
                    rectPaint.setStrokeWidth(-1);
                    rectPaint.setAntiAlias(true);
                    canvas->drawRect(rect, rectPaint);

                    SkPaint labelPaint;
                    labelPaint.setColor(color);
                    labelPaint.setAntiAlias(true);
                    sk_tool_utils::set_portable_typeface(&labelPaint);
                    labelPaint.setTextSize(10 * SK_Scalar1);
                    canvas->drawText(gStyles[style].fName,
                                        strlen(gStyles[style].fName),
                                        0, rect.height() + 12 * SK_Scalar1,
                                        labelPaint);
                    canvas->drawText(gFills[fill].fName,
                                        strlen(gFills[fill].fName),
                                        0, rect.height() + 24 * SK_Scalar1,
                                        labelPaint);
                    canvas->drawText(gCaps[cap].fName,
                                        strlen(gCaps[cap].fName),
                                        0, rect.height() + 36 * SK_Scalar1,
                                        labelPaint);
                }
                canvas->restore();
            }
            canvas->restore();
        }
        canvas->restore();
        canvas->restore();
    }
Example #5
0
static SkSize computeSize(const SkBitmap& bm, const SkMatrix& mat) {
    SkRect bounds = SkRect::MakeWH(SkIntToScalar(bm.width()),
                                   SkIntToScalar(bm.height()));
    mat.mapRect(&bounds);
    return SkSize::Make(bounds.width(), bounds.height());
}
void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
                       const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
                       const SkPaint* paint, SkRegion** outRegion) {
    if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) {
        return;
    }
    
    // if our canvas is GL, draw this as a mesh, which will be faster than
    // in parts (which is faster for raster)
    if (canvas && canvas->getViewport(NULL)) {
        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
                              chunk.xDivs, chunk.numXDivs,
                              chunk.yDivs, chunk.numYDivs,
                              paint);
        return;
    }

#ifdef USE_TRACE
    gTrace = true;
#endif

    SkASSERT(canvas || outRegion);

#if 0
    if (canvas) {
        const SkMatrix& m = canvas->getTotalMatrix();
        SkDebugf("ninepatch [%g %g %g] [%g %g %g]\n",
                 SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
                 SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
    }
#endif

#ifdef USE_TRACE
    if (gTrace) {
        SkDEBUGF(("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height())));
        SkDEBUGF(("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()));
        SkDEBUGF(("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1]));
        SkDEBUGF(("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1]));
    }
#endif

    if (bounds.isEmpty() ||
        bitmap.width() == 0 || bitmap.height() == 0 ||
        (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
    {
#ifdef USE_TRACE
        if (gTrace) SkDEBUGF(("======== abort ninepatch draw\n"));
#endif
        return;
    }
    
    // should try a quick-reject test before calling lockPixels 

    SkAutoLockPixels alp(bitmap);
    // after the lock, it is valid to check getPixels()
    if (bitmap.getPixels() == NULL)
        return;

    SkPaint defaultPaint;
    if (NULL == paint) {
        paint = &defaultPaint;
    }

    const bool hasXfer = paint->getXfermode() != NULL;
    SkRect      dst;
    SkIRect     src;

    const int32_t x0 = chunk.xDivs[0];
    const int32_t y0 = chunk.yDivs[0];
    const SkColor initColor = ((SkPaint*)paint)->getColor();
    const uint8_t numXDivs = chunk.numXDivs;
    const uint8_t numYDivs = chunk.numYDivs;
    int i;
    int j;
    int colorIndex = 0;
    uint32_t color;
    bool xIsStretchable;
    const bool initialXIsStretchable =  (x0 == 0);
    bool yIsStretchable = (y0 == 0);
    const int bitmapWidth = bitmap.width();
    const int bitmapHeight = bitmap.height();

    SkScalar* dstRights = (SkScalar*) alloca((numXDivs + 1) * sizeof(SkScalar));
    bool dstRightsHaveBeenCached = false;

    int numStretchyXPixelsRemaining = 0;
    for (i = 0; i < numXDivs; i += 2) {
        numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i];
    }
    int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
    int numStretchyYPixelsRemaining = 0;
    for (i = 0; i < numYDivs; i += 2) {
        numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i];
    }
    int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;

#if 0
    SkDebugf("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
             bitmap.width(), bitmap.height(),
             SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
             SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
             numXDivs, numYDivs);
#endif

    src.fTop = 0;
    dst.fTop = bounds.fTop;
    // The first row always starts with the top being at y=0 and the bottom
    // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
    // the first row is stretchable along the Y axis, otherwise it is fixed.
    // The last row always ends with the bottom being bitmap.height and the top
    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
    // the Y axis, otherwise it is fixed.
    //
    // The first and last columns are similarly treated with respect to the X
    // axis.
    //
    // The above is to help explain some of the special casing that goes on the
    // code below.

    // The initial yDiv and whether the first row is considered stretchable or
    // not depends on whether yDiv[0] was zero or not.
    for (j = yIsStretchable ? 1 : 0;
          j <= numYDivs && src.fTop < bitmapHeight;
          j++, yIsStretchable = !yIsStretchable) {
        src.fLeft = 0;
        dst.fLeft = bounds.fLeft;
        if (j == numYDivs) {
            src.fBottom = bitmapHeight;
            dst.fBottom = bounds.fBottom;
        } else {
            src.fBottom = chunk.yDivs[j];
            const int srcYSize = src.fBottom - src.fTop;
            if (yIsStretchable) {
                dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
                                                          srcYSize,
                                                          numStretchyYPixelsRemaining,
                                                          numFixedYPixelsRemaining);
                numStretchyYPixelsRemaining -= srcYSize;
            } else {
                dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
                numFixedYPixelsRemaining -= srcYSize;
            }
        }

        xIsStretchable = initialXIsStretchable;
        // The initial xDiv and whether the first column is considered
        // stretchable or not depends on whether xDiv[0] was zero or not.
        for (i = xIsStretchable ? 1 : 0;
              i <= numXDivs && src.fLeft < bitmapWidth;
              i++, xIsStretchable = !xIsStretchable) {
            color = chunk.colors[colorIndex++];
            if (i == numXDivs) {
                src.fRight = bitmapWidth;
                dst.fRight = bounds.fRight;
            } else {
                src.fRight = chunk.xDivs[i];
                if (dstRightsHaveBeenCached) {
                    dst.fRight = dstRights[i];
                } else {
                    const int srcXSize = src.fRight - src.fLeft;
                    if (xIsStretchable) {
                        dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
                                                                  srcXSize,
                                                                  numStretchyXPixelsRemaining,
                                                                  numFixedXPixelsRemaining);
                        numStretchyXPixelsRemaining -= srcXSize;
                    } else {
                        dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
                        numFixedXPixelsRemaining -= srcXSize;
                    }
                    dstRights[i] = dst.fRight;
                }
            }
            // If this horizontal patch is too small to be displayed, leave
            // the destination left edge where it is and go on to the next patch
            // in the source.
            if (src.fLeft >= src.fRight) {
                src.fLeft = src.fRight;
                continue;
            }
            // Make sure that we actually have room to draw any bits
            if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
                goto nextDiv;
            }
            // If this patch is transparent, skip and don't draw.
            if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
                if (outRegion) {
                    if (*outRegion == NULL) {
                        *outRegion = new SkRegion();
                    }
                    SkIRect idst;
                    dst.round(&idst);
                    //LOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
                    //     idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
                    (*outRegion)->op(idst, SkRegion::kUnion_Op);
                }
                goto nextDiv;
            }
            if (canvas) {
#if 0
                SkDebugf("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
                         src.fLeft, src.fTop, src.width(), src.height(),
                         SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
                         SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
                if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
                    SkDebugf("--- skip patch\n");
                }
#endif
                drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
                                  color, hasXfer);
            }

nextDiv:
            src.fLeft = src.fRight;
            dst.fLeft = dst.fRight;
        }
        src.fTop = src.fBottom;
        dst.fTop = dst.fBottom;
        dstRightsHaveBeenCached = true;
    }
}
Example #7
0
void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
                                                     size_t offset,
                                                     size_t vertexStride,
                                                     int outerVertexNum,
                                                     int innerVertexNum,
                                                     GrColor color,
                                                     const SkRect& devOutside,
                                                     const SkRect& devOutsideAssist,
                                                     const SkRect& devInside,
                                                     bool miterStroke,
                                                     bool degenerate,
                                                     bool tweakAlphaForCoverage) const {
    intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;

    // We create vertices for four nested rectangles. There are two ramps from 0 to full
    // coverage, one on the exterior of the stroke and the other on the interior.
    // The following pointers refer to the four rects, from outermost to innermost.
    SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
    SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
    SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
                                                  (2 * outerVertexNum + innerVertexNum) *
                                                  vertexStride);

#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
    // TODO: this only really works if the X & Y margins are the same all around
    // the rect (or if they are all >= 1.0).
    SkScalar inset;
    if (!degenerate) {
        inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
        inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
        inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
        if (miterStroke) {
            inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
        } else {
            inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
                                                       devInside.fBottom);
        }
        SkASSERT(inset >= 0);
    } else {
        // TODO use real devRect here
        inset = SkMinScalar(devOutside.width(), SK_Scalar1);
        inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
                                                          devOutsideAssist.height()));
    }
#else
    SkScalar inset;
    if (!degenerate) {
        inset = SK_ScalarHalf;
    } else {
        // TODO use real devRect here
        inset = SkMinScalar(devOutside.width(), SK_Scalar1);
        inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
                                                          devOutsideAssist.height()));
    }
#endif

    if (miterStroke) {
        // outermost
        set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
        // inner two
        set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
        if (!degenerate) {
            set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
            // innermost
            set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
        } else {
            // When the interior rect has become degenerate we smoosh to a single point
            SkASSERT(devInside.fLeft == devInside.fRight &&
                     devInside.fTop == devInside.fBottom);
            fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
                                devInside.fRight, devInside.fBottom, vertexStride);
            fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
                                devInside.fRight, devInside.fBottom, vertexStride);
        }
    } else {
        SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
        SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
                                                            (outerVertexNum + 4) *
                                                            vertexStride);
        // outermost
        set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
        set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
                      -SK_ScalarHalf);
        // outer one of the inner two
        set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
        set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist,  inset,  inset);
        if (!degenerate) {
            // inner one of the inner two
            set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
            // innermost
            set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
        } else {
            // When the interior rect has become degenerate we smoosh to a single point
            SkASSERT(devInside.fLeft == devInside.fRight &&
                     devInside.fTop == devInside.fBottom);
            fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
                                devInside.fRight, devInside.fBottom, vertexStride);
            fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
                                devInside.fRight, devInside.fBottom, vertexStride);
        }
    }

    // Make verts point to vertex color and then set all the color and coverage vertex attrs
    // values. The outermost rect has 0 coverage
    verts += sizeof(SkPoint);
    for (int i = 0; i < outerVertexNum; ++i) {
        if (tweakAlphaForCoverage) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
        }
    }

    // scale is the coverage for the the inner two rects.
    int scale;
    setup_scale(&scale, inset);

    float innerCoverage = GrNormalizeByteToFloat(scale);
    GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);

    verts += outerVertexNum * vertexStride;
    for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
        if (tweakAlphaForCoverage) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
        }
    }

    // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
    // scaled coverage
    verts += (outerVertexNum + innerVertexNum) * vertexStride;
    if (!degenerate) {
        innerCoverage = 0;
        scaledColor = 0;
    }

    for (int i = 0; i < innerVertexNum; ++i) {
        if (tweakAlphaForCoverage) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
        }
    }
}
void NativeImageSkia::drawPattern(
    GraphicsContext* context,
    const FloatRect& floatSrcRect,
    const FloatSize& scale,
    const FloatPoint& phase,
    CompositeOperator compositeOp,
    const FloatRect& destRect,
    WebBlendMode blendMode,
    const IntSize& repeatSpacing) const
{
    FloatRect normSrcRect = floatSrcRect;
    normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
    if (destRect.isEmpty() || normSrcRect.isEmpty())
        return; // nothing to draw

    SkMatrix totalMatrix = context->getTotalMatrix();
    AffineTransform ctm = context->getCTM();
    SkScalar ctmScaleX = ctm.xScale();
    SkScalar ctmScaleY = ctm.yScale();
    totalMatrix.preScale(scale.width(), scale.height());

    // Figure out what size the bitmap will be in the destination. The
    // destination rect is the bounds of the pattern, we need to use the
    // matrix to see how big it will be.
    SkRect destRectTarget;
    totalMatrix.mapRect(&destRectTarget, normSrcRect);

    float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
    float destBitmapHeight = SkScalarToFloat(destRectTarget.height());

    bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());

    // Compute the resampling mode.
    InterpolationQuality resampling;
    if (context->isAccelerated())
        resampling = InterpolationLow;
    else if (isLazyDecoded)
        resampling = InterpolationHigh;
    else
        resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
    resampling = limitInterpolationQuality(context, resampling);

    SkMatrix localMatrix;
    // We also need to translate it such that the origin of the pattern is the
    // origin of the destination rect, which is what WebKit expects. Skia uses
    // the coordinate system origin as the base for the pattern. If WebKit wants
    // a shifted image, it will shift it from there using the localMatrix.
    const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
    const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
    localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));

    RefPtr<SkShader> shader;
    SkFilterQuality filterLevel = static_cast<SkFilterQuality>(resampling);

    // Bicubic filter is only applied to defer-decoded images, see
    // NativeImageSkia::draw for details.
    if (resampling == InterpolationHigh && !isLazyDecoded) {
        // Do nice resampling.
        filterLevel = kNone_SkFilterQuality;
        float scaleX = destBitmapWidth / normSrcRect.width();
        float scaleY = destBitmapHeight / normSrcRect.height();
        SkRect scaledSrcRect;

        // Since we are resizing the bitmap, we need to remove the scale
        // applied to the pixels in the bitmap shader. This means we need
        // CTM * localMatrix to have identity scale. Since we
        // can't modify CTM (or the rectangle will be drawn in the wrong
        // place), we must set localMatrix's scale to the inverse of
        // CTM scale.
        localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);

        // The image fragment generated here is not exactly what is
        // requested. The scale factor used is approximated and image
        // fragment is slightly larger to align to integer
        // boundaries.
        SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    } else {
        // Because no resizing occurred, the shader transform should be
        // set to the pattern's transform, which just includes scale.
        localMatrix.preScale(scale.width(), scale.height());

        // No need to resample before drawing.
        SkBitmap srcSubset;
        bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    }

    SkPaint paint;
    paint.setShader(shader.get());
    paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
    paint.setColorFilter(context->colorFilter());
    paint.setFilterQuality(filterLevel);
    context->drawRect(destRect, paint);
}
Example #9
0
bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
                          const SkRect &src, SkBlurStyle style,
                          SkIPoint *margin, SkMask::CreateMode createMode) {
    int profile_size = SkScalarCeilToInt(6*sigma);

    int pad = profile_size/2;
    if (margin) {
        margin->set( pad, pad );
    }

    dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
                     SkScalarRoundToInt(src.fTop - pad),
                     SkScalarRoundToInt(src.fRight + pad),
                     SkScalarRoundToInt(src.fBottom + pad));

    dst->fRowBytes = dst->fBounds.width();
    dst->fFormat = SkMask::kA8_Format;
    dst->fImage = NULL;

    int             sw = SkScalarFloorToInt(src.width());
    int             sh = SkScalarFloorToInt(src.height());

    if (createMode == SkMask::kJustComputeBounds_CreateMode) {
        if (style == kInner_SkBlurStyle) {
            dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
                             SkScalarRoundToInt(src.fTop),
                             SkScalarRoundToInt(src.fRight),
                             SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
            dst->fRowBytes = sw;
        }
        return true;
    }
    uint8_t *profile = NULL;

    ComputeBlurProfile(sigma, &profile);
    SkAutoTDeleteArray<uint8_t> ada(profile);

    size_t dstSize = dst->computeImageSize();
    if (0 == dstSize) {
        return false;   // too big to allocate, abort
    }

    uint8_t*        dp = SkMask::AllocImage(dstSize);

    dst->fImage = dp;

    int dstHeight = dst->fBounds.height();
    int dstWidth = dst->fBounds.width();

    uint8_t *outptr = dp;

    SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
    SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);

    ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
    ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);

    for (int y = 0 ; y < dstHeight ; ++y) {
        for (int x = 0 ; x < dstWidth ; x++) {
            unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
            *(outptr++) = maskval;
        }
    }

    if (style == kInner_SkBlurStyle) {
        // now we allocate the "real" dst, mirror the size of src
        size_t srcSize = (size_t)(src.width() * src.height());
        if (0 == srcSize) {
            return false;   // too big to allocate, abort
        }
        dst->fImage = SkMask::AllocImage(srcSize);
        for (int y = 0 ; y < sh ; y++) {
            uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
            uint8_t *inner_scanline = dst->fImage + y*sw;
            memcpy(inner_scanline, blur_scanline, sw);
        }
        SkMask::FreeImage(dp);

        dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
                         SkScalarRoundToInt(src.fTop),
                         SkScalarRoundToInt(src.fRight),
                         SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
        dst->fRowBytes = sw;

    } else if (style == kOuter_SkBlurStyle) {
        for (int y = pad ; y < dstHeight-pad ; y++) {
            uint8_t *dst_scanline = dp + y*dstWidth + pad;
            memset(dst_scanline, 0, sw);
        }
    } else if (style == kSolid_SkBlurStyle) {
        for (int y = pad ; y < dstHeight-pad ; y++) {
            uint8_t *dst_scanline = dp + y*dstWidth + pad;
            memset(dst_scanline, 0xff, sw);
        }
    }
    // normal and solid styles are the same for analytic rect blurs, so don't
    // need to handle solid specially.

    return true;
}
Example #10
0
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform,
                          const SkIRect& bbox, SkScalar rasterScale)
        : fCanvasTransform(canvasTransform),
          fBBox(bbox),
          fPixelGeneration(0) {
    fInfo.fColorCount = 0;
    fInfo.fColors = NULL;
    fInfo.fColorOffsets = NULL;
    fShaderTransform = shader.getLocalMatrix();
    fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;

    fType = shader.asAGradient(&fInfo);

    if (fType == SkShader::kNone_GradientType) {
        SkMatrix matrix;
        if (shader.isABitmap(&fImage, &matrix, fImageTileModes)) {
            SkASSERT(matrix.isIdentity());
        } else {
            // Generic fallback for unsupported shaders:
            //  * allocate a bbox-sized bitmap
            //  * shade the whole area
            //  * use the result as a bitmap shader

            // bbox is in device space. While that's exactly what we want for sizing our bitmap,
            // we need to map it into shader space for adjustments (to match
            // SkPDFImageShader::Create's behavior).
            SkRect shaderRect = SkRect::Make(bbox);
            if (!inverse_transform_bbox(canvasTransform, &shaderRect)) {
                fImage.reset();
                return;
            }

            // Clamp the bitmap size to about 1M pixels
            static const SkScalar kMaxBitmapArea = 1024 * 1024;
            SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
            if (bitmapArea > kMaxBitmapArea) {
                rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
            }

            SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
                                         SkScalarRoundToInt(rasterScale * bbox.height()));
            SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(),
                                        SkIntToScalar(size.height()) / shaderRect.height());

            fImage.allocN32Pixels(size.width(), size.height());
            fImage.eraseColor(SK_ColorTRANSPARENT);

            SkPaint p;
            p.setShader(const_cast<SkShader*>(&shader));

            SkCanvas canvas(fImage);
            canvas.scale(scale.width(), scale.height());
            canvas.translate(-shaderRect.x(), -shaderRect.y());
            canvas.drawPaint(p);

            fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
            fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
        }
        fPixelGeneration = fImage.getGenerationID();
    } else {
        AllocateGradientInfoStorage();
        shader.asAGradient(&fInfo);
    }
}
Example #11
0
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->fImage.lockPixels();

    // The image shader pattern cell will be drawn into a separate device
    // in pattern cell space (no scaling on the bitmap, though there may be
    // translations so that all content is in the device, coordinates > 0).

    // Map clip bounds to shader space to ensure the device is large enough
    // to handle fake clamping.
    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect deviceBounds;
    deviceBounds.set(fState.get()->fBBox);
    if (!inverseTransformBBox(finalMatrix, &deviceBounds)) {
        return;
    }

    const SkBitmap* image = &fState.get()->fImage;
    SkRect bitmapBounds;
    image->getBounds(&bitmapBounds);

    // For tiling modes, the bounds should be extended to include the bitmap,
    // otherwise the bitmap gets clipped out and the shader is empty and awful.
    // For clamp modes, we're only interested in the clip region, whether
    // or not the main bitmap is in it.
    SkShader::TileMode tileModes[2];
    tileModes[0] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];
    if (tileModes[0] != SkShader::kClamp_TileMode ||
            tileModes[1] != SkShader::kClamp_TileMode) {
        deviceBounds.join(bitmapBounds);
    }

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()),
                                 SkScalarRound(deviceBounds.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);

    SkRect patternBBox;
    image->getBounds(&patternBBox);

    // Translate the canvas so that the bitmap origin is at (0, 0).
    canvas.translate(-deviceBounds.left(), -deviceBounds.top());
    patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
    // Undo the translation in the final matrix
    finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());

    // If the bitmap is out of bounds (i.e. clamp mode where we only see the
    // stretched sides), canvas will clip this out and the extraneous data
    // won't be saved to the PDF.
    canvas.drawBitmap(*image, 0, 0);

    SkScalar width = SkIntToScalar(image->width());
    SkScalar height = SkIntToScalar(image->height());

    // Tiling is implied.  First we handle mirroring.
    if (tileModes[0] == SkShader::kMirror_TileMode) {
        SkMatrix xMirror;
        xMirror.setScale(-1, 1);
        xMirror.postTranslate(2 * width, 0);
        canvas.drawBitmapMatrix(*image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        canvas.drawBitmapMatrix(*image, yMirror);
        patternBBox.fBottom += height;
    }
    if (tileModes[0] == SkShader::kMirror_TileMode &&
            tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix mirror;
        mirror.setScale(-1, -1);
        mirror.postTranslate(2 * width, 2 * height);
        canvas.drawBitmapMatrix(*image, mirror);
    }

    // Then handle Clamping, which requires expanding the pattern canvas to
    // cover the entire surfaceBBox.

    // If both x and y are in clamp mode, we start by filling in the corners.
    // (Which are just a rectangles of the corner colors.)
    if (tileModes[0] == SkShader::kClamp_TileMode &&
            tileModes[1] == SkShader::kClamp_TileMode) {
        SkPaint paint;
        SkRect rect;
        rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, deviceBounds.top(),
                                deviceBounds.right(), 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height,
                                deviceBounds.right(), deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1,
                                           image->height() - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(deviceBounds.left(), height,
                                0, deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, image->height() - 1));
            canvas.drawRect(rect, paint);
        }
    }

    // Then expand the left, right, top, then bottom.
    if (tileModes[0] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
        if (deviceBounds.left() < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-deviceBounds.left(), 1);
            leftMatrix.postTranslate(deviceBounds.left(), 0);
            canvas.drawBitmapMatrix(left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (deviceBounds.right() > width) {
            SkBitmap right;
            subset.offset(image->width() - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(deviceBounds.right() - width, 1);
            rightMatrix.postTranslate(width, 0);
            canvas.drawBitmapMatrix(right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(right, rightMatrix);
            }
            patternBBox.fRight = deviceBounds.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
        if (deviceBounds.top() < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
            topMatrix.postTranslate(0, deviceBounds.top());
            canvas.drawBitmapMatrix(top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (deviceBounds.bottom() > height) {
            SkBitmap bottom;
            subset.offset(0, image->height() - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
            bottomMatrix.postTranslate(0, height);
            canvas.drawBitmapMatrix(bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(bottom, bottomMatrix);
            }
            patternBBox.fBottom = deviceBounds.height();
        }
    }

    // Put the canvas into the pattern stream (fContent).
    SkAutoTUnref<SkStream> content(pattern.content());
    setData(content.get());
    SkPDFResourceDict* resourceDict = pattern.getResourceDict();
    resourceDict->getReferencedResources(fResources, &fResources, false);

    populate_tiling_pattern_dict(this, patternBBox,
                                 pattern.getResourceDict(), finalMatrix);

    fState.get()->fImage.unlockPixels();
}
Example #12
0
    void onDraw(SkCanvas* canvas) override {

        int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;

        SkRect r = { 0, 0, SkIntToScalar(size*2), SkIntToScalar(size*2) };

        static const char* gConfigNames[] = { "8888", "565", "4444" };

        static const bool           gFilters[] = { false, true };
        static const char*          gFilterNames[] = {     "point",                     "bilinear" };

        static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
        static const char*          gModeNames[] = {    "C",                    "R",                   "M" };

        SkScalar y = SkIntToScalar(24);
        SkScalar x = SkIntToScalar(10);

        for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
            for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                SkPaint p;
                SkString str;
                p.setAntiAlias(true);
                sk_tool_utils::set_portable_typeface_always(&p);
                p.setDither(true);
                str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);

                p.setTextAlign(SkPaint::kCenter_Align);
                canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);

                x += r.width() * 4 / 3;
            }
        }

        y += SkIntToScalar(16);

        for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
                x = SkIntToScalar(10);
                for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
                    for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                        SkPaint paint;
#if 1 // Temporary change to regen bitmap before each draw. This may help tracking down an issue
                        // on SGX where resizing NPOT textures to POT textures exhibits a driver bug.
                        if (!fPowerOfTwoSize) {
                            makebm(&fTexture[i], gColorTypes[i], size, size);
                        }
#endif
                        setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
                        paint.setDither(true);

                        canvas->save();
                        canvas->translate(x, y);
                        canvas->drawRect(r, paint);
                        canvas->restore();

                        x += r.width() * 4 / 3;
                    }
                }
                {
                    SkPaint p;
                    SkString str;
                    p.setAntiAlias(true);
                    sk_tool_utils::set_portable_typeface_always(&p);
                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
                }

                y += r.height() * 4 / 3;
            }
        }
    }
Example #13
0
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
#if PLATFORM(CHROMIUM)
    TRACE_EVENT("paintSkBitmap", platformContext, 0);
#endif
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setAlpha(platformContext->getNormalizedAlpha());
    paint.setLooper(platformContext->getDrawLooper());
    // only antialias if we're rotated or skewed
    paint.setAntiAlias(hasNon90rotation(platformContext));

    SkCanvas* canvas = platformContext->canvas();

    ResamplingMode resampling;
    if (platformContext->isAccelerated())
        resampling = RESAMPLE_LINEAR;
    else
        resampling = platformContext->printing() ? RESAMPLE_NONE :
            computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height()));
    if (resampling == RESAMPLE_NONE) {
      // FIXME: This is to not break tests (it results in the filter bitmap flag
      // being set to true). We need to decide if we respect RESAMPLE_NONE
      // being returned from computeResamplingMode.
        resampling = RESAMPLE_LINEAR;
    }
    resampling = limitResamplingMode(platformContext, resampling);
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    if (resampling == RESAMPLE_AWESOME)
        drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint);
    }
    platformContext->didDrawRect(destRect, paint, &bitmap.bitmap());
}
Example #14
0
void GrAARectRenderer::StrokeAARect(GrDrawTarget* target,
                                    const GrPipelineBuilder& pipelineBuilder,
                                    GrColor color,
                                    const SkMatrix& viewMatrix,
                                    const SkRect& rect,
                                    const SkRect& devRect,
                                    const SkStrokeRec& stroke) {
    SkVector devStrokeSize;
    SkScalar width = stroke.getWidth();
    if (width > 0) {
        devStrokeSize.set(width, width);
        viewMatrix.mapVectors(&devStrokeSize, 1);
        devStrokeSize.setAbs(devStrokeSize);
    } else {
        devStrokeSize.set(SK_Scalar1, SK_Scalar1);
    }

    const SkScalar dx = devStrokeSize.fX;
    const SkScalar dy = devStrokeSize.fY;
    const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
    const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);

    SkScalar spare;
    {
        SkScalar w = devRect.width() - dx;
        SkScalar h = devRect.height() - dy;
        spare = SkTMin(w, h);
    }

    SkRect devOutside(devRect);
    devOutside.outset(rx, ry);

    bool miterStroke = true;
    // For hairlines, make bevel and round joins appear the same as mitered ones.
    // small miter limit means right angles show bevel...
    if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
                        stroke.getMiter() < SK_ScalarSqrt2)) {
        miterStroke = false;
    }

    if (spare <= 0 && miterStroke) {
        FillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
        return;
    }

    SkRect devInside(devRect);
    devInside.inset(rx, ry);

    SkRect devOutsideAssist(devRect);

    // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
    // to draw the outer of the rect. Because there are 8 vertices on the outer
    // edge, while vertex number of inner edge is 4, the same as miter-stroke.
    if (!miterStroke) {
        devOutside.inset(0, ry);
        devOutsideAssist.outset(0, ry);
    }

    GeometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
                         devOutsideAssist, devInside, miterStroke);
}
Example #15
0
void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
                                          GrDrawState* drawState,
                                          GrColor color,
                                          const SkRect& rect,
                                          const SkMatrix& combinedMatrix,
                                          const SkRect& devRect) {
    GrDrawState::AutoRestoreEffects are(drawState);

    CoverageAttribType type;
    SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*drawState, color, &type));

    size_t vertexStride = gp->getVertexStride();
    GrDrawTarget::AutoReleaseGeometry geo(target, 8, vertexStride, 0);
    if (!geo.succeeded()) {
        SkDebugf("Failed to get space for vertices!\n");
        return;
    }

    if (NULL == fAAFillRectIndexBuffer) {
        fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx,
                                                                  kIndicesPerAAFillRect,
                                                                  kNumAAFillRectsInIndexBuffer,
                                                                  kVertsPerAAFillRect);
    }
    GrIndexBuffer* indexBuffer = fAAFillRectIndexBuffer;
    if (NULL == indexBuffer) {
        SkDebugf("Failed to create index buffer!\n");
        return;
    }

    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());

    SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);

    SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
    inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());

    if (combinedMatrix.rectStaysRect()) {
        // Temporarily #if'ed out. We don't want to pass in the devRect but
        // right now it is computed in GrContext::apply_aa_to_rect and we don't
        // want to throw away the work
#if 0
        SkRect devRect;
        combinedMatrix.mapRect(&devRect, rect);
#endif

        set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
        set_inset_fan(fan1Pos, vertexStride, devRect, inset,  inset);
    } else {
        // compute transformed (1, 0) and (0, 1) vectors
        SkVector vec[2] = {
          { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
          { combinedMatrix[SkMatrix::kMSkewX],  combinedMatrix[SkMatrix::kMScaleY] }
        };

        vec[0].normalize();
        vec[0].scale(SK_ScalarHalf);
        vec[1].normalize();
        vec[1].scale(SK_ScalarHalf);

        // create the rotated rect
        fan0Pos->setRectFan(rect.fLeft, rect.fTop,
                            rect.fRight, rect.fBottom, vertexStride);
        combinedMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);

        // Now create the inset points and then outset the original
        // rotated points

        // TL
        *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
            *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
        // BL
        *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
            *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
        // BR
        *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
            *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
        // TR
        *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
            *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
    }

    // Make verts point to vertex color and then set all the color and coverage vertex attrs values.
    verts += sizeof(SkPoint);
    for (int i = 0; i < 4; ++i) {
        if (kUseCoverage_CoverageAttribType == type) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
        }
    }

    int scale;
    if (inset < SK_ScalarHalf) {
        scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
        SkASSERT(scale >= 0 && scale <= 255);
    } else {
        scale = 0xff;
    }

    verts += 4 * vertexStride;

    float innerCoverage = GrNormalizeByteToFloat(scale);
    GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);

    for (int i = 0; i < 4; ++i) {
        if (kUseCoverage_CoverageAttribType == type) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
        }
    }

    target->setIndexSourceToBuffer(indexBuffer);
    target->drawIndexedInstances(drawState,
                                 gp,
                                 kTriangles_GrPrimitiveType,
                                 1,
                                 kVertsPerAAFillRect,
                                 kIndicesPerAAFillRect);
    target->resetIndexSource();
}
Example #16
0
 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min,
         SkScalar max) {
     return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min;
 }
Example #17
0
void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
                                    GrDrawState* drawState,
                                    GrColor color,
                                    const SkRect& rect,
                                    const SkMatrix& combinedMatrix,
                                    const SkRect& devRect,
                                    const SkStrokeRec& stroke) {
    SkVector devStrokeSize;
    SkScalar width = stroke.getWidth();
    if (width > 0) {
        devStrokeSize.set(width, width);
        combinedMatrix.mapVectors(&devStrokeSize, 1);
        devStrokeSize.setAbs(devStrokeSize);
    } else {
        devStrokeSize.set(SK_Scalar1, SK_Scalar1);
    }

    const SkScalar dx = devStrokeSize.fX;
    const SkScalar dy = devStrokeSize.fY;
    const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
    const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);

    // Temporarily #if'ed out. We don't want to pass in the devRect but
    // right now it is computed in GrContext::apply_aa_to_rect and we don't
    // want to throw away the work
#if 0
    SkRect devRect;
    combinedMatrix.mapRect(&devRect, rect);
#endif

    SkScalar spare;
    {
        SkScalar w = devRect.width() - dx;
        SkScalar h = devRect.height() - dy;
        spare = SkTMin(w, h);
    }

    SkRect devOutside(devRect);
    devOutside.outset(rx, ry);

    bool miterStroke = true;
    // For hairlines, make bevel and round joins appear the same as mitered ones.
    // small miter limit means right angles show bevel...
    if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
                        stroke.getMiter() < SK_ScalarSqrt2)) {
        miterStroke = false;
    }

    if (spare <= 0 && miterStroke) {
        this->fillAARect(target, drawState, color, devOutside, SkMatrix::I(), devOutside);
        return;
    }

    SkRect devInside(devRect);
    devInside.inset(rx, ry);

    SkRect devOutsideAssist(devRect);

    // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
    // to draw the outer of the rect. Because there are 8 vertices on the outer
    // edge, while vertex number of inner edge is 4, the same as miter-stroke.
    if (!miterStroke) {
        devOutside.inset(0, ry);
        devOutsideAssist.outset(0, ry);
    }

    this->geometryStrokeAARect(target, drawState, color, devOutside, devOutsideAssist, devInside,
                               miterStroke);
}
Example #18
0
static void generate_aa_fill_rect_geometry(intptr_t verts,
                                           size_t vertexStride,
                                           GrColor color,
                                           const SkMatrix& viewMatrix,
                                           const SkRect& rect,
                                           const SkRect& devRect,
                                           bool tweakAlphaForCoverage,
                                           const SkMatrix* localMatrix) {
    SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);

    SkScalar inset;

    if (viewMatrix.rectStaysRect()) {
        inset = SkMinScalar(devRect.width(), SK_Scalar1);
        inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());

        set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
        set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset);
    } else {
        // compute transformed (1, 0) and (0, 1) vectors
        SkVector vec[2] = {{viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY]},
                           {viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY]}};

        SkScalar len1 = SkPoint::Normalize(&vec[0]);
        vec[0].scale(SK_ScalarHalf);
        SkScalar len2 = SkPoint::Normalize(&vec[1]);
        vec[1].scale(SK_ScalarHalf);

        inset = SkMinScalar(len1 * rect.width(), SK_Scalar1);
        inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height());

        // create the rotated rect
        SkPointPriv::SetRectFan(fan0Pos, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
                vertexStride);
        SkMatrixPriv::MapPointsWithStride(viewMatrix, fan0Pos, vertexStride, 4);

        // Now create the inset points and then outset the original
        // rotated points

        // TL
        *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
        // BL
        *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
        // BR
        *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
        // TR
        *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
                *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
        *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
    }

    if (localMatrix) {
        SkMatrix invViewMatrix;
        if (!viewMatrix.invert(&invViewMatrix)) {
            SkDebugf("View matrix is non-invertible, local coords will be wrong.");
            invViewMatrix = SkMatrix::I();
        }
        SkMatrix localCoordMatrix;
        localCoordMatrix.setConcat(*localMatrix, invViewMatrix);
        SkPoint* fan0Loc = reinterpret_cast<SkPoint*>(verts + sizeof(SkPoint) + sizeof(GrColor));
        SkMatrixPriv::MapPointsWithStride(localCoordMatrix, fan0Loc, vertexStride, fan0Pos,
                                          vertexStride, 8);
    }

    // Make verts point to vertex color and then set all the color and coverage vertex attrs
    // values.
    verts += sizeof(SkPoint);

    // The coverage offset is always the last vertex attribute
    intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint);
    for (int i = 0; i < 4; ++i) {
        if (tweakAlphaForCoverage) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = 0;
        }
    }

    int scale;
    if (inset < SK_ScalarHalf) {
        scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
        SkASSERT(scale >= 0 && scale <= 255);
    } else {
        scale = 0xff;
    }

    verts += 4 * vertexStride;

    float innerCoverage = GrNormalizeByteToFloat(scale);
    GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);

    for (int i = 0; i < 4; ++i) {
        if (tweakAlphaForCoverage) {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
        } else {
            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
            *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = innerCoverage;
        }
    }
}
Example #19
0
bool SVGPaintServerGradient::setup(GraphicsContext*& context, 
    const RenderObject* object, 
    SVGPaintTargetType type, 
    bool isPaintingText) const
{
    m_ownerElement->buildGradient();

    RenderStyle* style = object->style();
    bool isFilled = 
        (type & ApplyToFillTargetType) && 
         style->svgStyle()->hasFill();
    bool isStroked = 
        (type & ApplyToStrokeTargetType) && 
         style->svgStyle()->hasStroke();

    if(!gradientStops().size())
        return false;

    if(gradientStops().size()==1) {
        context->setFillColor(gradientStops()[0].second);
        return true;
    }

    // Create a gradient builder helper to generate the data 
    // we'll need to provide Skia
    SkiaGradientBuilder builder(gradientStops(), 
        isFilled ? style->svgStyle()->fillOpacity() : 
                   style->svgStyle()->strokeOpacity());

    SkShader::TileMode tile_mode;

    // Convert SVG spread modes to Skia tile modes
    switch(spreadMethod())
    {
    default:
    case SPREADMETHOD_PAD:     
        tile_mode = SkShader::kClamp_TileMode; break;
    case SPREADMETHOD_REFLECT: 
        tile_mode = SkShader::kMirror_TileMode; break;
    case SPREADMETHOD_REPEAT:  
        tile_mode = SkShader::kRepeat_TileMode; break;
    }

    SkShader* shader = NULL;

    SkMatrix matrix;

    // Calculate a matrix to transform a gradient to fit the bounding box
    if (boundingBoxMode()) {
        matrix.reset();
        SkRect rc = context->getBoundingBoxForCurrentPath(true);

        matrix.preTranslate(rc.fLeft, rc.fTop);
        matrix.preScale(rc.width(), rc.height());
        matrix.preConcat(gradientTransform());
    } else
        matrix = gradientTransform();

    if (this->type() == LinearGradientPaintServer) {
        const SVGPaintServerLinearGradient* linear = 
            static_cast<const SVGPaintServerLinearGradient*>(this);

        SkPoint pts[2];

        pts[0].fX = linear->gradientStart().x();
        pts[0].fY = linear->gradientStart().y();
        pts[1].fX = linear->gradientEnd().x();
        pts[1].fY = linear->gradientEnd().y();

        shader = SkGradientShader::CreateLinear(pts, 
            builder.colors(), builder.pos(), builder.count(), tile_mode);
    } else if (this->type() == RadialGradientPaintServer) {
        const SVGPaintServerRadialGradient* radial = 
            static_cast<const SVGPaintServerRadialGradient*>(this);

        SkPoint center;
        SkScalar radius;

        center.fX = radial->gradientCenter().x();
        center.fY = radial->gradientCenter().y();
        radius = radial->gradientRadius();

        shader = SkGradientShader::CreateRadial(
            center, radius, builder.colors(), builder.pos(), 
            builder.count(), tile_mode);

    } else {
        return false;
    }

    if (isPaintingText) {
        if (isFilled) {
            context->setTextDrawingMode(cTextFill);
        }

        if (isStroked) {
            context->setTextDrawingMode(cTextStroke);
        }
    }

    if (isStroked) {
        applyStrokeStyleToContext(context, style, object);
    }

    if (shader) {
        shader->setLocalMatrix(matrix);
        context->platformContext()->setGradient(shader);

        return true;
    }

    return false;
}
Example #20
0
void TransparencyWin::computeLayerSize()
{
    if (m_transformMode == Untransform) {
        // The meaning of the "transformed" source rect is a little ambigous
        // here. The rest of the code doesn't care about it in the Untransform
        // case since we're using our own happy coordinate system. So we set it
        // to be the source rect since that matches how the code below actually
        // uses the variable: to determine how to translate things to account
        // for the offset of the layer.
        m_transformedSourceRect = m_sourceRect;
    } else if (m_transformMode == KeepTransform && m_layerMode != TextComposite) {
        // FIXME: support clipping for other modes
        IntRect clippedSourceRect = m_sourceRect;
        SkRect clipBounds;
        if (m_destContext->getClipBounds(&clipBounds)) {
            FloatRect clipRect(clipBounds.left(), clipBounds.top(), clipBounds.width(), clipBounds.height());
            clippedSourceRect.intersect(enclosingIntRect(clipRect));
        }
        m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect);
    } else
        m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect);

    m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height());
}
bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
                          const SkRect &src, Style style,
                          SkIPoint *margin, SkMask::CreateMode createMode) {
    int profile_size = SkScalarCeilToInt(6*sigma);

    int pad = profile_size/2;
    if (margin) {
        margin->set( pad, pad );
    }

    dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
                     SkScalarRoundToInt(src.fTop - pad),
                     SkScalarRoundToInt(src.fRight + pad),
                     SkScalarRoundToInt(src.fBottom + pad));

    dst->fRowBytes = dst->fBounds.width();
    dst->fFormat = SkMask::kA8_Format;
    dst->fImage = NULL;

    int             sw = SkScalarFloorToInt(src.width());
    int             sh = SkScalarFloorToInt(src.height());

    if (createMode == SkMask::kJustComputeBounds_CreateMode) {
        if (style == kInner_Style) {
            dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
                             SkScalarRoundToInt(src.fTop),
                             SkScalarRoundToInt(src.fRight),
                             SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
            dst->fRowBytes = sw;
        }
        return true;
    }
    unsigned int *profile = NULL;

    compute_profile(sigma, &profile);
    SkAutoTDeleteArray<unsigned int> ada(profile);

    size_t dstSize = dst->computeImageSize();
    if (0 == dstSize) {
        return false;   // too big to allocate, abort
    }

    uint8_t*        dp = SkMask::AllocImage(dstSize);

    dst->fImage = dp;

    int dstHeight = dst->fBounds.height();
    int dstWidth = dst->fBounds.width();

    // nearest odd number less than the profile size represents the center
    // of the (2x scaled) profile
    int center = ( profile_size & ~1 ) - 1;

    int w = sw - center;
    int h = sh - center;

    uint8_t *outptr = dp;

    SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);

    for (int x = 0 ; x < dstWidth ; ++x) {
        if (profile_size <= sw) {
            horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
        } else {
            float span = float(sw)/(2*sigma);
            float giX = 1.5f - (x+.5f)/(2*sigma);
            horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
        }
    }

    for (int y = 0 ; y < dstHeight ; ++y) {
        unsigned int profile_y;
        if (profile_size <= sh) {
            profile_y = profile_lookup(profile, y, dstHeight, h);
        } else {
            float span = float(sh)/(2*sigma);
            float giY = 1.5f - (y+.5f)/(2*sigma);
            profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span)));
        }

        for (int x = 0 ; x < dstWidth ; x++) {
            unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y);
            *(outptr++) = maskval;
        }
    }

    if (style == kInner_Style) {
        // now we allocate the "real" dst, mirror the size of src
        size_t srcSize = (size_t)(src.width() * src.height());
        if (0 == srcSize) {
            return false;   // too big to allocate, abort
        }
        dst->fImage = SkMask::AllocImage(srcSize);
        for (int y = 0 ; y < sh ; y++) {
            uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad;
            uint8_t *inner_scanline = dst->fImage + y*sw;
            memcpy(inner_scanline, blur_scanline, sw);
        }
        SkMask::FreeImage(dp);

        dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
                         SkScalarRoundToInt(src.fTop),
                         SkScalarRoundToInt(src.fRight),
                         SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds
        dst->fRowBytes = sw;

    } else if (style == kOuter_Style) {
        for (int y = pad ; y < dstHeight-pad ; y++) {
            uint8_t *dst_scanline = dp + y*dstWidth + pad;
            memset(dst_scanline, 0, sw);
        }
    } else if (style == kSolid_Style) {
        for (int y = pad ; y < dstHeight-pad ; y++) {
            uint8_t *dst_scanline = dp + y*dstWidth + pad;
            memset(dst_scanline, 0xff, sw);
        }
    }
    // normal and solid styles are the same for analytic rect blurs, so don't
    // need to handle solid specially.

    return true;
}
Example #22
0
// static
bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
                            const SkBitmap& source,
                            ResizeMethod method,
                            float destWidth, float destHeight,
                            SkBitmap::Allocator* allocator) {

  SkConvolutionProcs convolveProcs= { 0, NULL, NULL, NULL, NULL };
  PlatformConvolutionProcs(&convolveProcs);

  SkRect destSubset = { 0, 0, destWidth, destHeight };

  // Ensure that the ResizeMethod enumeration is sound.
    SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
        (method <= RESIZE_LAST_QUALITY_METHOD)) ||
        ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
        (method <= RESIZE_LAST_ALGORITHM_METHOD)));

    SkRect dest = { 0, 0, destWidth, destHeight };
    if (!dest.contains(destSubset)) {
        SkErrorInternals::SetError( kInvalidArgument_SkError,
                                    "Sorry, the destination bitmap scale subset "
                                    "falls outside the full destination bitmap." );
        return false;
    }

    // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
    // return empty.
    if (source.width() < 1 || source.height() < 1 ||
        destWidth < 1 || destHeight < 1) {
        // todo: seems like we could handle negative dstWidth/Height, since that
        // is just a negative scale (flip)
        return false;
    }

    method = ResizeMethodToAlgorithmMethod(method);

    // Check that we deal with an "algorithm methods" from this point onward.
    SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
        (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD));

    SkAutoLockPixels locker(source);
    if (!source.readyToDraw() ||
        source.colorType() != kN32_SkColorType) {
        return false;
    }

    SkResizeFilter filter(method, source.width(), source.height(),
                          destWidth, destHeight, destSubset, convolveProcs);

    // Get a source bitmap encompassing this touched area. We construct the
    // offsets and row strides such that it looks like a new bitmap, while
    // referring to the old data.
    const unsigned char* sourceSubset =
        reinterpret_cast<const unsigned char*>(source.getPixels());

    // Convolve into the result.
    SkBitmap result;
    result.setInfo(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()),
                                        SkScalarCeilToInt(destSubset.height()),
                                        source.alphaType()));
    result.allocPixels(allocator, NULL);
    if (!result.readyToDraw()) {
        return false;
    }

    BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()),
        !source.isOpaque(), filter.xFilter(), filter.yFilter(),
        static_cast<int>(result.rowBytes()),
        static_cast<unsigned char*>(result.getPixels()),
        convolveProcs, true);

    *resultPtr = result;
    resultPtr->lockPixels();
    SkASSERT(resultPtr->getPixels());
    return true;
}
Example #23
0
    void onDraw(SkCanvas* canvas) override {

        canvas->drawColor(SK_ColorGRAY);

        for (size_t i = 0; i < SK_ARRAY_COUNT(emojiFonts); ++i) {
            SkPaint paint;
            paint.setTypeface(emojiFonts[i].typeface);
            const char* text = emojiFonts[i].text;

            // draw text at different point sizes
            const int textSize[] = { 10, 30, 50, };
            const int textYOffset[] = { 10, 40, 100, };
            SkASSERT(sizeof(textSize) == sizeof(textYOffset));
            size_t y_offset = 0;
            for (size_t y = 0; y < sizeof(textSize) / sizeof(int); y++) {
                paint.setTextSize(SkIntToScalar(textSize[y]));
                canvas->drawText(text, strlen(text), 10, SkIntToScalar(textYOffset[y]), paint);
                y_offset += textYOffset[y];
            }

            // draw with shaders and image filters
            for (int makeLinear = 0; makeLinear < 2; makeLinear++) {
                for (int makeBlur = 0; makeBlur < 2; makeBlur++) {
                    for (int makeGray = 0; makeGray < 2; makeGray++) {
                        SkPaint shaderPaint;
                        shaderPaint.setTypeface(paint.getTypeface());
                        if (SkToBool(makeLinear)) {
                            shaderPaint.setShader(MakeLinear())->unref();
                        }

                        if (SkToBool(makeBlur) && SkToBool(makeGray)) {
                            SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(NULL));
                            SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, grayScale));
                            shaderPaint.setImageFilter(blur);
                        } else if (SkToBool(makeBlur)) {
                            SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f, NULL));
                            shaderPaint.setImageFilter(blur);
                        } else if (SkToBool(makeGray)) {
                            SkAutoTUnref<SkImageFilter> grayScale(make_grayscale(NULL));
                            shaderPaint.setImageFilter(grayScale);
                        }
                        shaderPaint.setTextSize(30);
                        canvas->drawText(text, strlen(text), 380, SkIntToScalar(y_offset),
                                         shaderPaint);
                        y_offset += 32;
                    }
                }
            }

            // setup work needed to draw text with different clips
            canvas->translate(10, 160);
            paint.setTextSize(40);

            // compute the bounds of the text
            SkRect bounds;
            paint.measureText(text, strlen(text), &bounds);

            const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf;
            const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf;
            const SkScalar boundsQuarterWidth = boundsHalfWidth * SK_ScalarHalf;
            const SkScalar boundsQuarterHeight = boundsHalfHeight * SK_ScalarHalf;

            SkRect upperLeftClip = SkRect::MakeXYWH(bounds.left(), bounds.top(),
                                                    boundsHalfWidth, boundsHalfHeight);
            SkRect lowerRightClip = SkRect::MakeXYWH(bounds.centerX(), bounds.centerY(),
                                                     boundsHalfWidth, boundsHalfHeight);
            SkRect interiorClip = bounds;
            interiorClip.inset(boundsQuarterWidth, boundsQuarterHeight);

            const SkRect clipRects[] = { bounds, upperLeftClip, lowerRightClip, interiorClip };

            SkPaint clipHairline;
            clipHairline.setColor(SK_ColorWHITE);
            clipHairline.setStyle(SkPaint::kStroke_Style);

            for (size_t x = 0; x < sizeof(clipRects) / sizeof(SkRect); ++x) {
                canvas->save();
                canvas->drawRect(clipRects[x], clipHairline);
                paint.setAlpha(0x20);
                canvas->drawText(text, strlen(text), 0, 0, paint);
                canvas->clipRect(clipRects[x]);
                paint.setAlpha(0xFF);
                canvas->drawText(text, strlen(text), 0, 0, paint);
                canvas->restore();
                canvas->translate(0, bounds.height() + SkIntToScalar(25));
            }
        }
    }
Example #24
0
    void onDraw(SkCanvas* canvas) override {
        float scale = 32.f/kPOTSize;

        int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;

        SkRect r = { 0, 0, SkIntToScalar(size*2), SkIntToScalar(size*2) };

        const char* gColorTypeNames[] = { "8888" , "565", "4444" };

        constexpr SkFilterQuality gFilterQualitys[] =
            { kNone_SkFilterQuality,
              kLow_SkFilterQuality,
              kMedium_SkFilterQuality,
              kHigh_SkFilterQuality };
        const char* gFilterNames[] = { "None", "Low", "Medium", "High" };

        constexpr SkShader::TileMode gModes[] = {
            SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
        const char* gModeNames[] = { "C", "R", "M" };

        SkScalar y = SkIntToScalar(24);
        SkScalar x = SkIntToScalar(10)/scale;

        for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
            for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                SkPaint p;
                SkString str;
                p.setAntiAlias(true);
                sk_tool_utils::set_portable_typeface(&p);
                str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);

                p.setTextAlign(SkPaint::kCenter_Align);
                canvas->drawText(str.c_str(), str.size(), scale*(x + r.width()/2), y, p);

                x += r.width() * 4 / 3;
            }
        }

        y = SkIntToScalar(40) / scale;

        for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilterQualitys); j++) {
                x = SkIntToScalar(10)/scale;
                for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
                    for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                        SkPaint paint;
#if 1 // Temporary change to regen bitmap before each draw. This may help tracking down an issue
      // on SGX where resizing NPOT textures to POT textures exhibits a driver bug.
                        if (!fPowerOfTwoSize) {
                            makebm(&fTexture[i], gColorTypes[i], size, size);
                        }
#endif
                        setup(&paint, fTexture[i], gFilterQualitys[j], gModes[kx], gModes[ky]);
                        paint.setDither(true);

                        canvas->save();
                        canvas->scale(scale,scale);
                        canvas->translate(x, y);
                        canvas->drawRect(r, paint);
                        canvas->restore();

                        x += r.width() * 4 / 3;
                    }
                }
                {
                    SkPaint p;
                    SkString str;
                    p.setAntiAlias(true);
                    sk_tool_utils::set_portable_typeface(&p);
                    str.printf("%s, %s", gColorTypeNames[i], gFilterNames[j]);
                    canvas->drawText(str.c_str(), str.size(), scale*x, scale*(y + r.height() * 2 / 3), p);
                }

                y += r.height() * 4 / 3;
            }
        }
    }
Example #25
0
DEF_TEST(CanvasState_test_complex_layers, reporter) {
    const int WIDTH = 400;
    const int HEIGHT = 400;
    const int SPACER = 10;

    SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
                                   SkIntToScalar(WIDTH-(2*SPACER)),
                                   SkIntToScalar((HEIGHT-(2*SPACER)) / 7));

    const SkColorType colorTypes[] = {
        kRGB_565_SkColorType, kN32_SkColorType
    };

    const int layerAlpha[] = { 255, 255, 0 };
    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
                                          SkCanvas::kARGB_NoClipLayer_SaveFlag
    };
    REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));

    bool (*drawFn)(SkCanvasState* state, float l, float t,
                   float r, float b, int32_t s);

    OpenLibResult openLibResult(reporter);
    if (openLibResult.handle() != NULL) {
        *(void**) (&drawFn) = dlsym(openLibResult.handle(),
                                    "complex_layers_draw_from_canvas_state");
    } else {
        drawFn = complex_layers_draw_from_canvas_state;
    }

    REPORTER_ASSERT(reporter, drawFn);
    if (!drawFn) {
        return;
    }

    for (size_t i = 0; i < SK_ARRAY_COUNT(colorTypes); ++i) {
        SkBitmap bitmaps[2];
        for (int j = 0; j < 2; ++j) {
            bitmaps[j].allocPixels(SkImageInfo::Make(WIDTH, HEIGHT,
                                                     colorTypes[i],
                                                     kPremul_SkAlphaType));

            SkCanvas canvas(bitmaps[j]);

            canvas.drawColor(SK_ColorRED);

            for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) {
                // draw a rect within the layer's bounds and again outside the layer's bounds
                canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]);

                if (j) {
                    // Capture from the first Skia.
                    SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
                    REPORTER_ASSERT(reporter, state);

                    // And draw to it in the second Skia.
                    bool success = complex_layers_draw_from_canvas_state(state,
                            rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, SPACER);
                    REPORTER_ASSERT(reporter, success);

                    // And release it in the *first* Skia.
                    SkCanvasStateUtils::ReleaseCanvasState(state);
                } else {
                    // Draw in the first Skia.
                    complex_layers_draw(&canvas, rect.fLeft, rect.fTop,
                                        rect.fRight, rect.fBottom, SPACER);
                }

                canvas.restore();

                // translate the canvas for the next iteration
                canvas.translate(0, 2*(rect.height() + SPACER));
            }
        }

        // now we memcmp the two bitmaps
        REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
        REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
                                          bitmaps[1].getPixels(),
                                          bitmaps[0].getSize()));
    }
}
Example #26
0
static void toString(const SkRect& r, SkString* str) {
    str->printf("[%g,%g %g:%g]",
                SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
                SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
}
void draw_paths(SkCanvas* canvas, ShadowMode mode) {
    SkTArray<SkPath> paths;
    paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
    SkRRect oddRRect;
    oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
    paths.push_back().addRRect(oddRRect);
    paths.push_back().addRect(SkRect::MakeWH(50, 50));
    paths.push_back().addCircle(25, 25, 25);
    paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
    paths.push_back().addOval(SkRect::MakeWH(20, 60));

    // star
    SkTArray<SkPath> concavePaths;
    concavePaths.push_back().moveTo(0.0f, -33.3333f);
    concavePaths.back().lineTo(9.62f, -16.6667f);
    concavePaths.back().lineTo(28.867f, -16.6667f);
    concavePaths.back().lineTo(19.24f, 0.0f);
    concavePaths.back().lineTo(28.867f, 16.6667f);
    concavePaths.back().lineTo(9.62f, 16.6667f);
    concavePaths.back().lineTo(0.0f, 33.3333f);
    concavePaths.back().lineTo(-9.62f, 16.6667f);
    concavePaths.back().lineTo(-28.867f, 16.6667f);
    concavePaths.back().lineTo(-19.24f, 0.0f);
    concavePaths.back().lineTo(-28.867f, -16.6667f);
    concavePaths.back().lineTo(-9.62f, -16.6667f);
    concavePaths.back().close();

    // dumbbell
    concavePaths.push_back().moveTo(50, 0);
    concavePaths.back().cubicTo(100, 25, 60, 50, 50, 0);
    concavePaths.back().cubicTo(0, -25, 40, -50, 50, 0);

    static constexpr SkScalar kPad = 15.f;
    static constexpr SkScalar kLightR = 100.f;
    static constexpr SkScalar kHeight = 50.f;

    // transform light position relative to canvas to handle tiling
    SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400);
    SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 };

    canvas->translate(3 * kPad, 3 * kPad);
    canvas->save();
    SkScalar x = 0;
    SkScalar dy = 0;
    SkTDArray<SkMatrix> matrices;
    matrices.push()->reset();
    SkMatrix* m = matrices.push();
    m->setRotate(33.f, 25.f, 25.f);
    m->postScale(1.2f, 0.8f, 25.f, 25.f);
    for (auto& m : matrices) {
        for (int flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
            for (const auto& path : paths) {
                SkRect postMBounds = path.getBounds();
                m.mapRect(&postMBounds);
                SkScalar w = postMBounds.width() + kHeight;
                SkScalar dx = w + kPad;
                if (x + dx > kW - 3 * kPad) {
                    canvas->restore();
                    canvas->translate(0, dy);
                    canvas->save();
                    x = 0;
                    dy = 0;
                }

                canvas->save();
                canvas->concat(m);

                if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
                    draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
                                true, flags);
                    draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
                                false, flags);
                } else if (kGrayscale == mode) {
                    SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
                    SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
                    SkShadowUtils::DrawShadow(canvas, path, SkPoint3{0, 0, kHeight}, lightPos,
                                              kLightR, ambientColor, spotColor, flags);
                }

                SkPaint paint;
                paint.setAntiAlias(true);
                if (kDebugColorNoOccluders == mode) {
                    // Draw the path outline in green on top of the ambient and spot shadows.
                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
                        paint.setColor(SK_ColorCYAN);
                    } else {
                        paint.setColor(SK_ColorGREEN);
                    }
                    paint.setStyle(SkPaint::kStroke_Style);
                    paint.setStrokeWidth(0);
                } else {
                    paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
                        paint.setAlpha(128);
                    }
                    paint.setStyle(SkPaint::kFill_Style);
                }
                canvas->drawPath(path, paint);
                canvas->restore();

                canvas->translate(dx, 0);
                x += dx;
                dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
            }
        }
    }

    // concave paths
    canvas->restore();
    canvas->translate(kPad, dy);
    canvas->save();
    x = kPad;
    dy = 0;
    for (auto& m : matrices) {
        // for the concave paths we are not clipping, so transparent and opaque are the same
        for (const auto& path : concavePaths) {
            SkRect postMBounds = path.getBounds();
            m.mapRect(&postMBounds);
            SkScalar w = postMBounds.width() + kHeight;
            SkScalar dx = w + kPad;

            canvas->save();
            canvas->concat(m);

            if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
                draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
                            true, kNone_ShadowFlag);
                draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
                            false, kNone_ShadowFlag);
            } else if (kGrayscale == mode) {
                SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
                SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
                SkShadowUtils::DrawShadow(canvas, path, SkPoint3{ 0, 0, kHeight }, lightPos,
                                          kLightR, ambientColor, spotColor, kNone_ShadowFlag);
            }

            SkPaint paint;
            paint.setAntiAlias(true);
            if (kDebugColorNoOccluders == mode) {
                // Draw the path outline in green on top of the ambient and spot shadows.
                paint.setColor(SK_ColorGREEN);
                paint.setStyle(SkPaint::kStroke_Style);
                paint.setStrokeWidth(0);
            } else {
                paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
                paint.setStyle(SkPaint::kFill_Style);
            }
            canvas->drawPath(path, paint);
            canvas->restore();

            canvas->translate(dx, 0);
            x += dx;
            dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
        }
    }

    // Show where the light is in x,y as a circle (specified in device space).
    SkMatrix invCanvasM = canvas->getTotalMatrix();
    if (invCanvasM.invert(&invCanvasM)) {
        canvas->save();
        canvas->concat(invCanvasM);
        SkPaint paint;
        paint.setColor(SK_ColorBLACK);
        paint.setAntiAlias(true);
        canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
        canvas->restore();
    }
}
Example #28
0
    virtual void onDraw(SkCanvas* canvas) {

        SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };

        static const char* gConfigNames[] = { "8888", "565", "4444" };

        static const bool           gFilters[] = { false, true };
        static const char*          gFilterNames[] = {     "point",                     "bilinear" };

        static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
        static const char*          gModeNames[] = {    "C",                    "R",                   "M" };

        SkScalar y = SkIntToScalar(24);
        SkScalar x = SkIntToScalar(10);

        for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
            for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                SkPaint p;
                SkString str;
                p.setAntiAlias(true);
                p.setDither(true);
                p.setLooper(&fLooper);
                str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);

                p.setTextAlign(SkPaint::kCenter_Align);
                canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);

                x += r.width() * 4 / 3;
            }
        }

        y += SkIntToScalar(16);

        for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
                x = SkIntToScalar(10);
                for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
                    for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
                        SkPaint paint;
                        setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
                        paint.setDither(true);

                        canvas->save();
                        canvas->translate(x, y);
                        canvas->drawRect(r, paint);
                        canvas->restore();

                        x += r.width() * 4 / 3;
                    }
                }
                {
                    SkPaint p;
                    SkString str;
                    p.setAntiAlias(true);
                    p.setLooper(&fLooper);
                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
                }

                y += r.height() * 4 / 3;
            }
        }
    }
static void test_complex_layers(skiatest::Reporter* reporter) {
    const int WIDTH = 400;
    const int HEIGHT = 400;
    const int SPACER = 10;

    SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
                                   SkIntToScalar(WIDTH-(2*SPACER)),
                                   SkIntToScalar((HEIGHT-(2*SPACER)) / 7));

    const SkBitmap::Config configs[] = { SkBitmap::kRGB_565_Config,
                                         SkBitmap::kARGB_8888_Config
    };
    const int configCount = sizeof(configs) / sizeof(SkBitmap::Config);

    const int layerAlpha[] = { 255, 255, 0 };
    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
                                          SkCanvas::kARGB_NoClipLayer_SaveFlag
    };
    REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));
    const int layerCombinations = sizeof(layerAlpha) / sizeof(int);

    for (int i = 0; i < configCount; ++i) {
        SkBitmap bitmaps[2];
        for (int j = 0; j < 2; ++j) {
            bitmaps[j].setConfig(configs[i], WIDTH, HEIGHT);
            bitmaps[j].allocPixels();

            SkCanvas canvas(bitmaps[j]);

            canvas.drawColor(SK_ColorRED);

            for (int k = 0; k < layerCombinations; ++k) {
                // draw a rect within the layer's bounds and again outside the layer's bounds
                canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]);

                SkCanvasState* state = NULL;
                SkCanvas* tmpCanvas = NULL;
                if (j) {
                    state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
                    REPORTER_ASSERT(reporter, state);
                    tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state);
                    REPORTER_ASSERT(reporter, tmpCanvas);
                } else {
                    tmpCanvas = SkRef(&canvas);
                }

                SkPaint bluePaint;
                bluePaint.setColor(SK_ColorBLUE);
                bluePaint.setStyle(SkPaint::kFill_Style);

                tmpCanvas->drawRect(rect, bluePaint);
                tmpCanvas->translate(0, rect.height() + SPACER);
                tmpCanvas->drawRect(rect, bluePaint);

                tmpCanvas->unref();
                SkCanvasStateUtils::ReleaseCanvasState(state);

                canvas.restore();

                // translate the canvas for the next iteration
                canvas.translate(0, 2*(rect.height() + SPACER));
            }
        }

        // now we memcmp the two bitmaps
        REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
        REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
                                          bitmaps[1].getPixels(),
                                          bitmaps[0].getSize()));
    }
}
Example #30
0
    virtual void onDraw(SkCanvas* canvas) {
        struct FillAndName {
            SkPath::FillType fFill;
            const char*      fName;
        };
        static const FillAndName gFills[] = {
            {SkPath::kWinding_FillType, "Winding"},
            {SkPath::kEvenOdd_FillType, "Even / Odd"},
            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
        };
        struct StyleAndName {
            SkPaint::Style fStyle;
            const char*    fName;
        };
        static const StyleAndName gStyles[] = {
            {SkPaint::kFill_Style, "Fill"},
            {SkPaint::kStroke_Style, "Stroke"},
            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
        };

        SkPaint titlePaint;
        titlePaint.setColor(SK_ColorBLACK);
        titlePaint.setAntiAlias(true);
        titlePaint.setLCDRenderText(true);
        titlePaint.setTextSize(15 * SK_Scalar1);
        const char title[] = "Empty Paths Drawn Into Rectangle Clips With "
                             "Indicated Style and Fill";
        canvas->drawText(title, strlen(title),
                            20 * SK_Scalar1,
                            20 * SK_Scalar1,
                            titlePaint);

        SkRandom rand;
        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
        int i = 0;
        canvas->save();
        canvas->translate(10 * SK_Scalar1, 0);
        canvas->save();
        for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                if (0 == i % 4) {
                    canvas->restore();
                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
                    canvas->save();
                } else {
                    canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                }
                ++i;


                SkColor color = rand.nextU();
                color = 0xff000000| color; // force solid
                this->drawEmpty(canvas, color, rect,
                                gStyles[style].fStyle, gFills[fill].fFill);

                SkPaint rectPaint;
                rectPaint.setColor(SK_ColorBLACK);
                rectPaint.setStyle(SkPaint::kStroke_Style);
                rectPaint.setStrokeWidth(-1);
                rectPaint.setAntiAlias(true);
                canvas->drawRect(rect, rectPaint);

                SkPaint labelPaint;
                labelPaint.setColor(color);
                labelPaint.setAntiAlias(true);
                labelPaint.setLCDRenderText(true);
                labelPaint.setTextSize(12 * SK_Scalar1);
                canvas->drawText(gStyles[style].fName,
                                 strlen(gStyles[style].fName),
                                 0, rect.height() + 15 * SK_Scalar1,
                                 labelPaint);
                canvas->drawText(gFills[fill].fName,
                                 strlen(gFills[fill].fName),
                                 0, rect.height() + 28 * SK_Scalar1,
                                 labelPaint);
            }
        }
        canvas->restore();
        canvas->restore();
    }