1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "clip_utils.h"
17#include "draw_utils.h"
18#include "gfx_utils/diagram/depiction/depict_curve.h"
19#include "gfx_utils/diagram/rasterizer/rasterizer_scanline_antialias.h"
20#include "gfx_utils/diagram/scanline/geometry_scanline.h"
21#include "gfx_utils/diagram/spancolorfill/fill_base.h"
22#include "gfx_utils/diagram/spancolorfill/fill_pattern_rgba.h"
23#include "gfx_utils/graphic_log.h"
24#include "render/render_base.h"
25#include "render/render_pixfmt_rgba_blend.h"
26
27namespace OHOS {
28using UICanvasPath = DepictCurve;
29using PathTransform = DepictTransform<UICanvasPath>;
30
31ClipPath& ClipPath::MoveTo(const PointF& point)
32{
33    vertices_->MoveTo(point.x, point.y);
34    return *this;
35}
36
37ClipPath& ClipPath::LineTo(const PointF& point)
38{
39    if (vertices_->GetTotalVertices() != 0) {
40        vertices_->LineTo(point.x, point.y);
41    } else {
42        vertices_->MoveTo(point.x, point.y);
43    }
44    return *this;
45}
46
47ClipPath& ClipPath::CurveTo(const PointF& control1, const PointF& control2, const PointF& end)
48{
49    vertices_->CubicBezierCurve(control1.x, control1.y, control2.x, control2.y, end.x, end.y);
50    return *this;
51}
52
53ClipPath& ClipPath::Arc(const PointF& center, float radius, int16_t startAngle, int16_t endAngle)
54{
55    if (startAngle == endAngle) {
56        return *this;
57    }
58    float sinma = radius * Sin(startAngle);
59    float cosma = radius * Sin(QUARTER_IN_DEGREE - startAngle);
60    if (vertices_->GetTotalVertices() != 0) {
61        vertices_->LineTo(float(center.x + sinma), float(center.y - cosma));
62    } else {
63        vertices_->MoveTo(float(center.x + sinma), float(center.y - cosma));
64    }
65    if (MATH_ABS(startAngle - endAngle) < CIRCLE_IN_DEGREE) {
66        sinma = radius * Sin(endAngle);
67        cosma = radius * Sin(QUARTER_IN_DEGREE - endAngle);
68    } else {
69        Circle(center, radius);
70        return *this;
71    }
72
73    int16_t angle = endAngle - startAngle;
74    bool largeArcFlag = false;
75    if (angle > SEMICIRCLE_IN_DEGREE || angle <= 0) {
76        largeArcFlag = true;
77    }
78    vertices_->ArcTo(radius, radius, angle, largeArcFlag, 1, center.x + sinma, center.y - cosma);
79    return *this;
80}
81
82ClipPath& ClipPath::Circle(const PointF& center, float radius)
83{
84    if (radius <= 0) {
85        return *this;
86    }
87    vertices_->RemoveAll();
88#if defined(GRAPHIC_ENABLE_BEZIER_ARC_FLAG) && GRAPHIC_ENABLE_BEZIER_ARC_FLAG
89    BezierArc arc(center.x, center.y, radius, radius, 0, TWO_TIMES * PI);
90    vertices_->ConcatPath(arc, 0);
91#endif
92    return *this;
93}
94
95UICanvasVertices& ClipUtils::CloseVertices(const ClipPath& path)
96{
97    UICanvasVertices& vertices = path.GetVertices();
98    vertices.ClosePolygon();
99    return vertices;
100}
101
102void ClipUtils::PerformScan(const ClipPath& path, const ImageInfo* imageInfo)
103{
104    RasterizerScanlineAntialias rasterizer;
105    GeometryScanline scanline;
106    TransAffine transform;
107    UICanvasVertices& vertices = CloseVertices(path);
108    UICanvasPath canvasPath(vertices);
109    PathTransform pathTransform(canvasPath, transform);
110    rasterizer.Reset();
111    rasterizer.AddPath(pathTransform);
112    if (!rasterizer.RewindScanlines()) {
113        for (int32_t i = 0; i < imageInfo->header.height; i++) {
114            DrawHorLine(0, i, imageInfo->header.width, OPA_TRANSPARENT, imageInfo);
115        }
116        return;
117    }
118    scanline.Reset(rasterizer.GetMinX(), rasterizer.GetMaxX());
119
120    bool first = true;
121    int16_t y = 0;
122    while (rasterizer.SweepScanline(scanline)) {
123        y = scanline.GetYLevel();
124        if (first) {
125            for (int32_t i = 0; i < y; i++) {
126                DrawHorLine(0, i, imageInfo->header.width, OPA_TRANSPARENT, imageInfo);
127            }
128            first = false;
129        }
130        uint32_t numSpans = scanline.NumSpans();
131        GeometryScanline::ConstIterator span = scanline.Begin();
132        int16_t index = 0;
133        while (true) {
134            int32_t x = span->x;
135            int32_t len = span->spanLength;
136            const uint8_t* covers = span->covers;
137
138            if (len < 0) {
139                len = -len;
140            }
141            DrawHorLine(index, y, x - index - 1, OPA_TRANSPARENT, imageInfo);
142            for (int16_t i = x; i < x + len; i++, covers++) {
143                DrawPixel(i, y, *covers, imageInfo);
144            }
145            index = x + len;
146            if (--numSpans == 0) {
147                break;
148            }
149            ++span;
150        }
151        DrawHorLine(index, y, imageInfo->header.width - index, OPA_TRANSPARENT, imageInfo);
152    }
153
154    for (int32_t i = y + 1; i < imageInfo->header.height; i++) {
155        DrawHorLine(0, i, imageInfo->header.width, OPA_TRANSPARENT, imageInfo);
156    }
157}
158
159void ClipUtils::DrawPixel(int16_t x, int16_t y, uint8_t opa, const ImageInfo* imageInfo)
160{
161    if (x < 0 || x > imageInfo->header.width - 1 || y < 0 || y > imageInfo->header.height - 1) {
162        return;
163    }
164
165    int32_t offset = imageInfo->header.width * y + x;
166    switch (imageInfo->header.colorMode) {
167        case ARGB8888: {
168            Color32* buffer = reinterpret_cast<Color32*>(const_cast<uint8_t*>(imageInfo->data));
169            buffer[offset].alpha = buffer[offset].alpha * opa / OPA_OPAQUE;
170            break;
171        }
172        default: {
173            GRAPHIC_LOGE("Only images in ARGB8888 format are supported!");
174            break;
175        }
176    }
177}
178
179void ClipUtils::DrawHorLine(int16_t x, int16_t y, int16_t width, uint8_t opa, const ImageInfo* imageInfo)
180{
181    for (int32_t i = x; i <= x + width; i++) {
182        DrawPixel(i, y, opa, imageInfo);
183    }
184}
185};
186