提交 7b667d28 编写于 作者: O openharmony_ci 提交者: Gitee

!150 Add clip function

Merge pull request !150 from jsjzju/master
......@@ -126,6 +126,7 @@ shared_library("ui") {
"frameworks/dock/screen_device_proxy.cpp",
"frameworks/dock/vibrator_manager.cpp",
"frameworks/dock/virtual_input_device.cpp",
"frameworks/draw/clip_utils.cpp",
"frameworks/draw/draw_arc.cpp",
"frameworks/draw/draw_curve.cpp",
"frameworks/draw/draw_image.cpp",
......
/*
* Copyright (c) 2021-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clip_utils.h"
#include <cmath>
#include "draw_utils.h"
#include "gfx_utils/graphic_log.h"
namespace OHOS {
namespace {
static const float EPS = 0.01f;
};
#define I_PART(X) ((int)(X))
#define F_PART(X) (((float)(X)) - (float)I_PART(X))
#define RF_PART(X) (1.0 - F_PART(X))
#define SWAP_FLOAT(a, b) \
do { \
float tmp = a; \
a = b; \
b = tmp; \
} while(0)
/*
* Note that the square of the distance from point B to the straight line AD is dB,
* and the square of the distance from point C to the straight line AD is dC.
* When (dB <= EPS && dC <= EPS), the line AD is considered to fit the spline.
*/
bool ClipPath::CheckoutSplineError(const Spline& spline) const
{
float dx = spline.d.x - spline.a.x;
float dy = spline.d.y - spline.a.y;
float c = dx * spline.a.y - dy * spline.a.x;
float deltB = dy * spline.b.x - dx * spline.b.y + c;
deltB = deltB * deltB;
float deltC = dy * spline.c.x - dx * spline.c.y + c;
deltC = deltC * deltC;
float delt = EPS * (dx * dx + dy * dy);
if (deltB <= delt && deltC <= delt) {
return true;
}
return false;
}
void ClipPath::MidPointOfLine(const PointF& a, const PointF& b, PointF& mid) const
{
// 2: half
mid.x = a.x + (b.x - a.x) / 2;
mid.y = a.y + (b.y - a.y) / 2;
}
void ClipPath::SplitSpline(Spline& s1, Spline& s2) const
{
PointF ab, bc, cd;
PointF abbc, bccd;
PointF e;
MidPointOfLine(s1.a, s1.b, ab);
MidPointOfLine(s1.b, s1.c, bc);
MidPointOfLine(s1.c, s1.d, cd);
MidPointOfLine(ab, bc, abbc);
MidPointOfLine(bc, cd, bccd);
MidPointOfLine(abbc, bccd, e);
s2.a = e;
s2.b = bccd;
s2.c = cd;
s2.d = s1.d;
s1.b = ab;
s1.c = abbc;
s1.d = e;
}
void ClipPath::SplineDecompose(Spline& s, ClipPolygon& polygon) const
{
List<Spline> list;
list.PushBack(s);
while (!list.IsEmpty()) {
Spline s1 = list.Front();
list.PopFront();
if (CheckoutSplineError(s1)) {
polygon.AddPoint(s1.a);
} else {
Spline s2;
SplitSpline(s1, s2);
list.PushFront(s2);
list.PushFront(s1);
}
}
polygon.AddPoint(s.d);
}
void ClipPath::GeneratePolygon(ClipPolygon& polygon) const
{
ListNode<PointF>* pointIter = points_.Begin();
ListNode<ClipPathCmd>* iter = cmd_.Begin();
for (; iter != cmd_.End() && pointIter != points_.End(); iter = iter->next_) {
switch (iter->data_) {
case CMD_MOVE_TO: {
if (!polygon.points_.IsEmpty()) {
return;
}
polygon.AddPoint(startPos_);
break;
}
case CMD_LINE_TO: {
PointF end = pointIter->data_;
pointIter = pointIter->next_;
polygon.AddPoint(end);
break;
}
case CMD_CURVE_TO: {
if (polygon.points_.IsEmpty()) {
return;
}
Spline s;
s.a = polygon.points_.Back();
s.b = pointIter->data_;
pointIter = pointIter->next_;
if (pointIter == points_.End()) {
return;
}
s.c = pointIter->data_;
pointIter = pointIter->next_;
if (pointIter == points_.End()) {
return;
}
s.d = pointIter->data_;
pointIter = pointIter->next_;
SplineDecompose(s, polygon);
break;
}
default:
break;
}
}
}
ClipPath& ClipPath::MoveTo(const PointF& point)
{
startPos_ = point;
/* If the previous command is also CMD_MOVE_TO, the previous command is overwritten. */
if ((cmd_.Size() != 0) && (cmd_.Tail()->data_ == CMD_MOVE_TO)) {
points_.Tail()->data_ = point;
return *this;
}
cmd_.PushBack(CMD_MOVE_TO);
return *this;
}
ClipPath& ClipPath::LineTo(const PointF& point)
{
if (cmd_.Size() == 0) {
MoveTo(point);
} else {
points_.PushBack(point);
cmd_.PushBack(CMD_LINE_TO);
}
return *this;
}
ClipPath& ClipPath::CurveTo(const PointF& control1, const PointF& control2, const PointF& end)
{
if (cmd_.Size() == 0) {
MoveTo(end);
} else {
points_.PushBack(control1);
points_.PushBack(control2);
points_.PushBack(end);
cmd_.PushBack(CMD_CURVE_TO);
}
return *this;
}
/* The ArcInner function can be used to fit the arc within 90 degrees by Bezier curve */
void ClipPath::ArcInner(const PointF& center, float radius, int16_t startAngle, int16_t endAngle)
{
if (radius > 0) {
float sinA = radius * Sin(startAngle);
float cosA = radius * Sin(QUARTER_IN_DEGREE - startAngle);
float sinB = radius * Sin(endAngle);
float cosB = radius * Sin(QUARTER_IN_DEGREE - endAngle);
float x0 = center.x + cosA;
float y0 = center.y + sinA;
float x3 = center.x + cosB;
float y3 = center.y + sinB;
int16_t addAngle = endAngle - startAngle;
// a = 4 * tan(angle / 4) / 3;
float a = 4.0 * Sin(addAngle / 4) / Sin(QUARTER_IN_DEGREE - addAngle / 4) / 3.0;
float x1 = x0 - a * (y0 - center.y);
float y1 = y0 + a * (x0 - center.x);
float x2 = x3 + a * (y3 - center.y);
float y2 = y3 - a * (x3 - center.x);
if (cmd_.Size() != 0) {
LineTo({x0, y0});
} else {
MoveTo({x0, y0});
}
CurveTo({x1, y1}, {x2, y2}, {x3, y3});
}
}
ClipPath& ClipPath::Arc(const PointF& center, float radius, int16_t startAngle, int16_t endAngle)
{
if (radius > 0) {
startAngle = (startAngle % CIRCLE_IN_DEGREE + CIRCLE_IN_DEGREE) % CIRCLE_IN_DEGREE;
endAngle = (endAngle % CIRCLE_IN_DEGREE + CIRCLE_IN_DEGREE) % CIRCLE_IN_DEGREE;
if (startAngle > endAngle) {
endAngle += CIRCLE_IN_DEGREE;
}
int16_t tmpAngle = QUARTER_IN_DEGREE;
while (startAngle < endAngle) {
while (tmpAngle <= startAngle) {
tmpAngle += QUARTER_IN_DEGREE;
}
if (tmpAngle > endAngle) {
tmpAngle = endAngle;
}
ArcInner(center, radius, startAngle, tmpAngle);
startAngle = tmpAngle;
}
}
return *this;
}
ClipPath& ClipPath::Circle(const PointF& center, float radius)
{
if (radius > 0) {
/*
* h(a) = (4 / 3) * (1 - cos(a / 2)) / sin(a / 2)
* h(90) = 0.552
*/
float h = 0.552 * radius;
float x0 = center.x + radius;
float y0 = center.y;
float x3 = center.x;
float y3 = center.y + radius;
float x1 = x0;
float y1 = y0 + h;
float x2 = x3 + h;
float y2 = y3;
MoveTo({x0, y0});
CurveTo({x1, y1}, {x2, y2}, {x3, y3});
x0 = x3;
y0 = y3;
x3 = center.x - radius;
y3 = center.y;
x1 = x0 - h;
y1 = y0;
x2 = x3;
y2 = y3 + h;
CurveTo({x1, y1}, {x2, y2}, {x3, y3});
x0 = x3;
y0 = y3;
x3 = center.x;
y3 = center.y - radius;
x1 = x0;
y1 = y0 - h;
x2 = x3 - h;
y2 = y3;
CurveTo({x1, y1}, {x2, y2}, {x3, y3});
x0 = x3;
y0 = y3;
x3 = center.x + radius;
y3 = center.y;
x1 = x0 + h;
y1 = y0;
x2 = x3;
y2 = y3 - h;
CurveTo({x1, y1}, {x2, y2}, {x3, y3});
}
return *this;
}
void ClipImageBlitter::DrawHorSpan(const List<Span>& span, int16_t yCur)
{
if (src_ == nullptr) {
return;
}
for (int16_t y = iy_; y < yCur; y++) {
DrawHorLine(0, y, src_->header.width, OPA_TRANSPARENT);
}
int16_t index = 0;
auto iter = span.Begin();
while (iter != span.End()) {
DrawHorLine(index, yCur, iter->data_.left - index, OPA_TRANSPARENT);
DrawHorLine(iter->data_.left, yCur, iter->data_.right - iter->data_.left + 1, iter->data_.opa);
index = iter->data_.right + 1;
iter = iter->next_;
}
DrawHorLine(index, yCur, src_->header.width - index, OPA_TRANSPARENT);
iy_ = yCur + 1;
}
void ClipImageBlitter::Finish()
{
if (src_ == nullptr) {
return;
}
for (int16_t y = iy_; y < src_->header.height; y++) {
DrawHorLine(0, y, src_->header.width, OPA_TRANSPARENT);
}
}
void ClipImageBlitter::DrawPixel(int16_t x, int16_t y, uint8_t opa)
{
if (x < 0 || x > src_->header.width - 1 || y < 0 || y > src_->header.height - 1) {
return;
}
int32_t offset = src_->header.width * y + x;
switch (src_->header.colorMode) {
case ARGB8888: {
Color32* buffer = reinterpret_cast<Color32*>(const_cast<uint8_t*>(src_->data));
buffer[offset].alpha = buffer[offset].alpha * opa / OPA_OPAQUE;
break;
}
default: {
GRAPHIC_LOGE("Only images in ARGB8888 format are supported!");
break;
}
}
}
void ClipImageBlitter::DrawHorLine(int16_t x, int16_t y, int16_t width, uint8_t opa)
{
if (width < 0 || opa == OPA_OPAQUE) {
return;
}
for (int16_t i = 0; i < width; i++) {
DrawPixel(x + i, y, opa);
}
}
void ClipUtils::CreateNewSpan(List<Span>& list, ListNode<Span>* node, int16_t left, int16_t right, uint8_t opa)
{
Span insert;
insert.left = left;
insert.right = right;
insert.opa = opa;
list.Insert(node, insert);
}
void ClipUtils::InsertSpan(List<Span>& curList, int16_t left, int16_t right, uint8_t opa)
{
if (left > right || opa == OPA_TRANSPARENT) {
return;
}
auto iter = curList.Begin();
auto endIt = curList.End();
while (iter != endIt) {
Span& curSpan = iter->data_;
if (right < curSpan.left) {
break;
}
if (left > curSpan.right) {
iter = iter->next_;
continue;
}
if (left < curSpan.left) {
CreateNewSpan(curList, iter, left, curSpan.left - 1, opa);
} else if (left > curSpan.left) {
CreateNewSpan(curList, iter, curSpan.left, left - 1, curSpan.opa);
curSpan.left = left;
}
if (right > curSpan.right) {
curSpan.opa = MATH_MAX(curSpan.opa, opa);
left = curSpan.right + 1;
iter = iter->next_;
continue;
} else if (right < curSpan.right) {
CreateNewSpan(curList, iter->next_, right + 1, curSpan.right, curSpan.opa);
curSpan.right = right;
}
curSpan.opa = MATH_MAX(curSpan.opa, opa);
return;
}
CreateNewSpan(curList, iter, left, right, opa);
}
void ClipUtils::MergeSpan(List<Span>& s)
{
auto iter = s.Begin();
auto endIt = s.End();
while (iter != endIt) {
auto next = iter->next_;
while (next != endIt) {
if (next->data_.left - 1 == iter->data_.left && next->data_.opa == iter->data_.opa) {
iter->data_.right = next->data_.right;
next = s.Remove(next);
continue;
}
break;
}
iter = iter->next_;
}
}
void ClipUtils::ClearSpanTable()
{
if (span0_ != nullptr) {
span0_->Clear();
delete span0_;
span0_ = nullptr;
}
if (span1_ != nullptr) {
span1_->Clear();
delete span1_;
span1_ = nullptr;
}
}
void ClipUtils::DrawAntiAliasedPoints(int16_t yCur)
{
auto iter = aaList_.Begin();
while (iter != aaList_.End()) {
if (iter->data_.y >= yCur + 1) {
break;
}
bool remove = false;
while (iter->data_.y < yCur + 1) {
uint8_t opa = MATH_ROUND(RF_PART(iter->data_.y) * OPA_OPAQUE);
InsertSpan(*span0_, iter->data_.x, iter->data_.x, opa);
InsertSpan(*span1_, iter->data_.x, iter->data_.x, OPA_OPAQUE - opa);
iter->data_.y += iter->data_.dy;
iter->data_.x += iter->data_.sx;
iter->data_.xIndex--;
if (iter->data_.xIndex <= 0) {
iter = aaList_.Remove(iter);
remove = true;
break;
}
}
if (!remove) {
iter = iter->next_;
}
}
}
void ClipUtils::CreateEdgeList(const ClipPath& path)
{
ClipPolygon polygon;
path.GeneratePolygon(polygon);
Graphic::Vector<PointF>& points = polygon.points_;
maxY_ = floor(polygon.bound_.bottom);
minY_ = ceil(polygon.bound_.top);
uint16_t size = points.Size();
for (int16_t i = 0; i < size; i++) {
float x1 = points[i].x;
float y1 = points[i].y;
float x2 = points[(i + 1) % size].x;
float y2 = points[(i + 1) % size].y;
if (y1 > y2) {
SWAP_FLOAT(x1, x2);
SWAP_FLOAT(y1, y2);
}
/* Insert the non steep edges to aaList_ */
bool steep = MATH_ABS(y1 - y2) > MATH_ABS(x1 - x2);
if (!steep) {
AAEdge edge;
float dy = (y1 - y2) / (x1 - x2);
edge.dy = MATH_ABS(dy);
if (x1 > x2) {
edge.x = floor(x1);
edge.y = y1 + (x1 - edge.x) * edge.dy;
edge.sx = -1;
edge.xIndex = edge.x - ceil(x2) + 1;
} else {
edge.x = ceil(x1);
edge.y = y1 + (edge.x - x1) * edge.dy;
edge.sx = 1;
edge.xIndex = floor(x2) - edge.x + 1;
}
if (edge.xIndex > 0) {
auto it = aaList_.Begin();
while (it != aaList_.End()) {
if (edge.y <= it->data_.y) {
break;
}
it = it->next_;
}
aaList_.Insert(it, edge);
}
}
/* Insert the edges to edgeList_ */
int16_t ymax = floor(y2);
int16_t ymin = floor(y1) + 1;
if (ymax < ymin) {
continue;
}
float dx = (x1 - x2) / (y1 - y2);
Edge p;
p.ymax = ymax;
p.ymin = ymin;
p.x = x1 + dx * (ymin - y1);
p.dx = dx;
auto iter = edgeList_.Begin();
while (iter != edgeList_.End()) {
if (p.ymin <= iter->data_.ymin) {
break;
}
iter = iter->next_;
}
edgeList_.Insert(iter, p);
}
}
void ClipUtils::PerformScan(const ClipPath& path, Blitter& blitter)
{
CreateEdgeList(path);
span0_ = new List<Span>();
span1_ = new List<Span>();
List<Edge> activeEdgeList;
for (int16_t i = minY_; i <= maxY_; ++i) {
/* Calculate the anti aliasing point of non steep line in each row */
DrawAntiAliasedPoints(i);
/*
* When the ymin of the edge is equal to the current line, the edge is inserted into the AET.
* Sort by x ascending (Sort by dx ascending when x is equal)
*/
auto iter1 = edgeList_.Begin();
while (iter1 != edgeList_.End()) {
if (iter1->data_.ymin == i) {
auto iter2 = activeEdgeList.Begin();
while (iter2 != activeEdgeList.End()) {
if (iter1->data_.x < iter2->data_.x) {
break;
}
if (iter1->data_.x == iter2->data_.x && iter1->data_.dx < iter2->data_.dx) {
break;
}
iter2 = iter2->next_;
}
activeEdgeList.Insert(iter2, iter1->data_);
iter1 = edgeList_.Remove(iter1);
continue;
}
break;
}
/* Traverse the activeEdgeList, pair the edges and insert the spans */
auto iter = activeEdgeList.Begin();
auto endIt = activeEdgeList.End();
while (iter != endIt && iter->next_ != endIt) {
int16_t xLeft = ceil(iter->data_.x);
int16_t xRight = floor(iter->next_->data_.x);
InsertSpan(*span0_, xLeft, xRight, OPA_OPAQUE);
/* Anti aliasing on both sides */
uint8_t opa = MATH_ROUND(RF_PART(iter->data_.x) * OPA_OPAQUE);
InsertSpan(*span0_, iter->data_.x, iter->data_.x, opa);
InsertSpan(*span0_, iter->data_.x + 1, iter->data_.x + 1, OPA_OPAQUE - opa);
opa = MATH_ROUND(RF_PART(iter->next_->data_.x) * OPA_OPAQUE);
InsertSpan(*span0_, iter->next_->data_.x, iter->next_->data_.x, opa);
InsertSpan(*span0_, iter->next_->data_.x + 1, iter->next_->data_.x + 1, OPA_OPAQUE - opa);
iter = iter->next_->next_;
}
/* Remove the edge with (ymax == i) in active edge list */
iter = activeEdgeList.Begin();
while (iter != endIt) {
if (iter->data_.ymax == i) {
iter = activeEdgeList.Remove(iter);
} else {
iter->data_.x += iter->data_.dx;
iter = iter->next_;
}
}
/* Merge the span of current row and do blit */
MergeSpan(*span0_);
blitter.DrawHorSpan(*span0_, i);
/* Clear the span of current row, then swap span0_ and span1_ */
span0_->Clear();
List<Span>* tmp;
tmp = span0_;
span0_ = span1_;
span1_ = tmp;
}
blitter.Finish();
ClearSpanTable();
}
};
\ No newline at end of file
/*
* Copyright (c) 2021-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GRAPHIC_LITE_CLIP_UTILS_H
#define GRAPHIC_LITE_CLIP_UTILS_H
#include "gfx_utils/geometry2d.h"
#include "gfx_utils/image_info.h"
#include "gfx_utils/list.h"
#include "gfx_utils/vector.h"
namespace OHOS {
/* Indicates an interval with the same transparency */
struct Span : public HeapBase {
int16_t left;
int16_t right;
uint8_t opa;
};
/* Indicates a point in float */
struct PointF {
float x;
float y;
};
/* Indicates a rectangle in flaot*/
struct RectF {
float left;
float top;
float right;
float bottom;
};
/* Indicates an edge */
struct Edge : public HeapBase {
/* The maximum y coordinate of the edge */
int16_t ymax;
/* The minimum y coordinate of the edge */
int16_t ymin;
/* The x-coordinate of the intersection with the current row */
float x;
/* Increment of x-coordinate when y-coordinate increases by 1 */
float dx;
};
/* Indicates an edge that to be antialiased */
struct AAEdge : public HeapBase {
/* The y-coordinate of current integer x-coordinate */
float y;
/* Increment of y-coordinate when x-coordinate step by sx */
float dy;
/* Current integer x-coordinate */
int16_t x;
/* Step of x, 1 or -1 */
int16_t sx;
/* Upper limit of steps */
int16_t xIndex;
};
/* Indicates a cubic Bezier curve */
struct Spline : public HeapBase {
/* Start point */
PointF a;
/* First control point */
PointF b;
/* Second control point */
PointF c;
/* End point */
PointF d;
};
enum ClipPathCmd {
CMD_MOVE_TO,
CMD_LINE_TO,
CMD_CURVE_TO,
};
class ClipPolygon : public HeapBase {
public:
ClipPolygon() {}
~ClipPolygon()
{
Clear();
}
void Clear()
{
points_.Clear();
bound_ = {0, 0, 0, 0};
}
void AddPoint(const PointF& p)
{
if (points_.IsEmpty()) {
bound_.left = p.x;
bound_.right = p.x;
bound_.top = p.y;
bound_.bottom = p.y;
points_.PushBack(p);
} else if (!MATH_FLT_EQUAL(points_.Back().x, p.x) || !MATH_FLT_EQUAL(points_.Back().y, p.y)) {
bound_.left = (p.x < bound_.left) ? p.x : bound_.left;
bound_.top = (p.y < bound_.top) ? p.y : bound_.top;
bound_.right = (p.x > bound_.right) ? p.x : bound_.right;
bound_.bottom = (p.y > bound_.bottom) ? p.y : bound_.bottom;
points_.PushBack(p);
}
}
Graphic::Vector<PointF> points_;
RectF bound_ = {0, 0, 0, 0};
};
/*
* Indicates a path to be cliped.
* Note: The path will be automatically closed. Only non-self-intersecting path are supported.
*/
class ClipPath : public HeapBase {
public:
ClipPath() {}
~ClipPath()
{
points_.Clear();
cmd_.Clear();
}
void GeneratePolygon(ClipPolygon& polygon) const;
ClipPath& MoveTo(const PointF& point);
ClipPath& LineTo(const PointF& point);
ClipPath& CurveTo(const PointF& control1, const PointF& control2, const PointF& end);
ClipPath& Arc(const PointF& center, float radius, int16_t startAngle, int16_t endAngle);
ClipPath& Circle(const PointF& center, float radius);
private:
bool CheckoutSplineError(const Spline& spline) const;
void MidPointOfLine(const PointF& a, const PointF& b, PointF& mid) const;
void SplitSpline(Spline& s1, Spline& s2) const;
void SplineDecompose(Spline& s, ClipPolygon& polygon) const;
void ArcInner(const PointF& center, float radius, int16_t startAngle, int16_t endAngle);
List<PointF> points_;
List<ClipPathCmd> cmd_;
PointF startPos_ = {0, 0};
};
/*
* Blitter and its subclasses are responible for handling the span list and actually draws the pixels
*/
class Blitter : public HeapBase {
public:
Blitter() {}
virtual ~Blitter() {}
virtual void DrawHorSpan(const List<Span>& span, int16_t yCur) {}
virtual void Finish() {}
};
/* Clipimageblitter will clip the image on its original memory */
class ClipImageBlitter : public Blitter {
public:
explicit ClipImageBlitter(const ImageInfo* src) : src_(src) {}
virtual ~ClipImageBlitter() {}
void DrawHorSpan(const List<Span>& span, int16_t yCur) override;
void Finish() override;
private:
void DrawPixel(int16_t x, int16_t y, uint8_t opa);
void DrawHorLine(int16_t x, int16_t y, int16_t width, uint8_t opa);
const ImageInfo* src_ = nullptr;
int16_t iy_ = 0;
};
class ClipUtils : public HeapBase {
public:
ClipUtils() {}
~ClipUtils() {}
void PerformScan(const ClipPath& path, Blitter& blitter);
private:
void ClearSpanTable();
void CreateNewSpan(List<Span>& list, ListNode<Span>* node, int16_t left, int16_t right, uint8_t opa);
void InsertSpan(List<Span>& curList, int16_t left, int16_t right, uint8_t opa);
void MergeSpan(List<Span>& s);
void CreateEdgeList(const ClipPath& path);
void DrawAntiAliasedPoints(int16_t yCur);
/* Save the lines that needs to be scanned vertically */
List<Edge> edgeList_;
/* Save the non steep lines that need to be antialiased */
List<AAEdge> aaList_;
/* Pointer to the span of the current row */
List<Span>* span0_ = nullptr;
/* Pointer to the span of the next row */
List<Span>* span1_ = nullptr;
int16_t minY_ = INT16_MAX;
int16_t maxY_ = INT16_MIN;
};
};
#endif
\ No newline at end of file
......@@ -24,6 +24,7 @@ test_sources = [
"../uitest/test_canvas/ui_test_canvas.cpp",
"../uitest/test_chart/ui_test_chart_pillar.cpp",
"../uitest/test_chart/ui_test_chart_polyline.cpp",
"../uitest/test_clip/ui_test_clip.cpp",
"../uitest/test_dialog/ui_test_dialog.cpp",
"../uitest/test_digital_clock/ui_test_digital_clock.cpp",
"../uitest/test_draw_line/ui_test_draw_line.cpp",
......
......@@ -24,6 +24,7 @@
#include "test_canvas/ui_test_canvas.h"
#include "test_chart/ui_test_chart_pillar.h"
#include "test_chart/ui_test_chart_polyline.h"
#include "test_clip/ui_test_clip.h"
#ifdef ENABLE_WINDOW
#include "test_dialog/ui_test_dialog.h"
#endif
......@@ -84,6 +85,7 @@ void UITestGroup::AddTestCase(TestCaseInfo testCaseInfo)
void UITestGroup::SetUpTestCase()
{
testCaseList_.PushBack(TestCaseInfo{"Clip", new UITestClip()});
#if ENABLE_ROTATE_INPUT
testCaseList_.PushBack(TestCaseInfo{"Rotate_Input", new UITestRotateInput()});
#endif
......
/*
* Copyright (c) 2021-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ui_test_clip.h"
#include <cmath>
#include "common/image.h"
#include "draw/clip_utils.h"
#include "gfx_utils/graphic_math.h"
#include "securec.h"
#include "test_resource_config.h"
namespace OHOS {
namespace {
static float g_radius1 = 40.f;
static int16_t g_startAngle = 0;
static int16_t g_endAngle = 50;
}
void UITestClip::SetUp()
{
if (container_ == nullptr) {
container_ = new UIScrollView();
container_->Resize(Screen::GetInstance().GetWidth(), Screen::GetInstance().GetHeight() - BACK_BUTTON_HEIGHT);
container_->SetHorizontalScrollState(false);
container_->SetThrowDrag(true);
}
positionY_ = 0;
}
void UITestClip::TearDown()
{
DeleteChildren(container_);
container_ = nullptr;
}
UIView* UITestClip::GetTestView()
{
UIKitClipTest001();
UIKitClipTest002();
UIKitClipTest003();
UIKitClipTest004();
UIKitClipTest005();
return container_;
}
bool UITestClip::OnClick(UIView& view, const ClickEvent& event)
{
bool caseChange1 = false;
bool caseChange2 = false;
if (&view == btnRadiusInc1_) {
g_radius1 += RADIUS_STEP_1;
caseChange1 = true;
} else if (&view == btnRadiusInc5_) {
g_radius1 += RADIUS_STEP_5;
caseChange1 = true;
} else if (&view == btnRadiusDec1_) {
g_radius1 -= RADIUS_STEP_1;
caseChange1 = true;
} else if (&view == btnRadiusDec5_) {
g_radius1 -= RADIUS_STEP_5;
caseChange1 = true;
} else if (&view == btnStartAngleInc_) {
g_startAngle += ANGLE_STEP;
caseChange2 = true;
} else if (&view == btnStartAngleDec_) {
g_startAngle -= ANGLE_STEP;
caseChange2 = true;
} else if (&view == btnEndAngleInc_) {
g_endAngle += ANGLE_STEP;
caseChange2 = true;
} else if (&view == btnEndAngleDec_) {
g_endAngle -= ANGLE_STEP;
caseChange2 = true;
}
if (caseChange1) {
char buffer[BUFFER_SIZE];
if (sprintf_s(buffer, BUFFER_SIZE, "当前半径 = %.0f", g_radius1) < 0) {
return false;
}
radiusText_->SetText(buffer);
ClipPath path;
// {70, 50}: center of circle
path.Circle({70, 50}, g_radius1);
imageView1_->SetSrc(JPEG_IMAGE_PATH);
ClipImage(imageView1_, path);
imageView1_->Invalidate();
}
if (caseChange2) {
ClipPath path;
// {80, 80}: center; 50: radius
path.MoveTo({80, 80}).Arc({80, 80}, 50, g_startAngle, g_endAngle);
imageView2_->SetSrc(JPEG_IMAGE_PATH);
ClipImage(imageView2_, path);
imageView2_->Invalidate();
}
return true;
}
void UITestClip::CreateTitleLabel(const char* title)
{
UILabel* titleLabel = new UILabel();
titleLabel->SetPosition(TEXT_DISTANCE_TO_LEFT_SIDE, positionY_, Screen::GetInstance().GetWidth(), TITLE_HEIGHT);
titleLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, FONT_DEFAULT_SIZE);
titleLabel->SetText(title);
container_->Add(titleLabel);
positionY_ += TITLE_HEIGHT + 8; // 8: gap
}
UIImageView* UITestClip::CreateImageView()
{
UIViewGroup* viewGroup = new UIViewGroup();
viewGroup->SetHeight(BLOCK_HEIGHT);
viewGroup->SetWidth(BLOCK_WIDTH);
viewGroup->SetPosition(VIEW_DISTANCE_TO_LEFT_SIDE, positionY_);
viewGroup->SetStyle(STYLE_BACKGROUND_COLOR, Color::Gray().full);
container_->Add(viewGroup);
positionY_ += BLOCK_HEIGHT + GAP;
UIImageView* imageVIew = new UIImageView();
imageVIew->SetPosition(IMAGE_POSITION_X, IMAGE_POSITION_Y);
imageVIew->SetSrc(JPEG_IMAGE_PATH);
viewGroup->Add(imageVIew);
return imageVIew;
}
void UITestClip::SetUpButton(UILabelButton* btn, const char* title, int16_t x, int16_t y)
{
if (btn == nullptr) {
return;
}
container_->Add(btn);
btn->SetPosition(x, y, BUTTON_WIDHT2, BUTTON_HEIGHT2);
btn->SetText(title);
btn->SetFont(DEFAULT_VECTOR_FONT_FILENAME, BUTTON_LABEL_SIZE);
btn->SetOnClickListener(this);
btn->SetStyleForState(STYLE_BORDER_RADIUS, BUTTON_STYLE_BORDER_RADIUS_VALUE, UIButton::RELEASED);
btn->SetStyleForState(STYLE_BORDER_RADIUS, BUTTON_STYLE_BORDER_RADIUS_VALUE, UIButton::PRESSED);
btn->SetStyleForState(STYLE_BORDER_RADIUS, BUTTON_STYLE_BORDER_RADIUS_VALUE, UIButton::INACTIVE);
btn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::RELEASED);
btn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::PRESSED);
btn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::INACTIVE);
}
void UITestClip::ClipImage(UIImageView* imageView, ClipPath& path)
{
const ImageInfo* info = imageView->GetImageInfo();
ClipImageBlitter blitter(info);
ClipUtils clip;
clip.PerformScan(path, blitter);
}
void UITestClip::UIKitClipTest001()
{
if (container_ == nullptr) {
return;
}
CreateTitleLabel("圆形裁剪 ");
int16_t x = VIEW_DISTANCE_TO_LEFT_SIDE + BLOCK_WIDTH + GAP;
int16_t y = positionY_;
btnRadiusInc1_ = new UILabelButton();
SetUpButton(btnRadiusInc1_, "半径+1", x, y);
btnRadiusDec1_ = new UILabelButton();
SetUpButton(btnRadiusDec1_, "半径-1", x + BUTTON_WIDHT2 + GAP, y);
btnRadiusInc5_ = new UILabelButton();
y += BUTTON_HEIGHT2 + GAP;
SetUpButton(btnRadiusInc5_, "半径+5", x, y);
btnRadiusDec5_ = new UILabelButton();
SetUpButton(btnRadiusDec5_, "半径-5", x + BUTTON_WIDHT2 + GAP, y);
y += BUTTON_HEIGHT2 + GAP;
radiusText_ = new UILabel();
radiusText_->SetPosition(x, y, Screen::GetInstance().GetWidth(), TITLE_HEIGHT);
radiusText_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, FONT_DEFAULT_SIZE);
char buffer[BUFFER_SIZE];
if (sprintf_s(buffer, BUFFER_SIZE, "当前半径 = %.0f", g_radius1) >= 0) {
radiusText_->SetText(buffer);
}
container_->Add(radiusText_);
ClipPath path;
// {70, 50}: center of circle
path.Circle({70, 50}, g_radius1);
imageView1_ = CreateImageView();
ClipImage(imageView1_, path);
}
void UITestClip::UIKitClipTest002()
{
if (container_ == nullptr) {
return;
}
CreateTitleLabel("弧形裁剪 ");
int16_t x = VIEW_DISTANCE_TO_LEFT_SIDE + BLOCK_WIDTH + GAP;
int16_t y = positionY_;
btnStartAngleInc_ = new UILabelButton();
SetUpButton(btnStartAngleInc_, "起始角度+", x, y);
btnStartAngleDec_ = new UILabelButton();
SetUpButton(btnStartAngleDec_, "起始角度-", x + BUTTON_WIDHT2 + GAP, y);
btnEndAngleInc_ = new UILabelButton();
y += BUTTON_HEIGHT2 + GAP;
SetUpButton(btnEndAngleInc_, "结束角度+", x, y);
btnEndAngleDec_ = new UILabelButton();
SetUpButton(btnEndAngleDec_, "结束角度-", x + BUTTON_WIDHT2 + GAP, y);
y += BUTTON_HEIGHT2 + GAP;
ClipPath path;
// {80, 80}: center; 50: radius
path.MoveTo({80, 80}).Arc({80, 80}, 50, g_startAngle, g_endAngle);
imageView2_ = CreateImageView();
ClipImage(imageView2_, path);
}
void UITestClip::UIKitClipTest003()
{
if (container_ == nullptr) {
return;
}
CreateTitleLabel("多边形裁剪----五角星 ");
int32_t rot = 0;
// 80: Radius of outer circle
int32_t outerR = 80;
// 40: Radius of inner circle
int32_t innerR = 40;
// 50: The x-coordinate of the starting point
int32_t x = 50;
// 50: The y-coordinate of the starting point
int32_t y = 50;
ClipPath path;
// 5: Needs to calculate five vertices
for(int32_t i = 0; i < 5; i++) {
// 18, 72: constant; 180.0: The angle corresponding to PI
path.LineTo({static_cast<float>(cos((18 + 72 * i - rot) / 180.0 * UI_PI) * outerR + x),
static_cast<float>(-sin((18 + 72 * i - rot ) / 180.0 * UI_PI) * outerR + y)});
// 54, 72: constant; 180.0: The angle corresponding to PI
path.LineTo({static_cast<float>(cos((54 + 72 * i - rot) / 180.0 * UI_PI) * innerR + x),
static_cast<float>(-sin((54 + 72 * i - rot ) / 180.0 * UI_PI) * innerR + y)});
}
UIImageView* imageView = CreateImageView();
ClipImage(imageView, path);
}
void UITestClip::UIKitClipTest004()
{
if (container_ == nullptr) {
return;
}
CreateTitleLabel("贝塞尔曲线裁剪 ");
ClipPath path;
// {50, 50}, {100, 50}, {100, 100}, {50, 100}: path points; {60, 110}, {80, 10}: control points of curve
path.MoveTo({50, 50}).CurveTo({60, 110}, {80, 10}, {100, 50}).LineTo({100, 100}).LineTo({50, 100});
UIImageView* imageView = CreateImageView();
ClipImage(imageView, path);
}
void UITestClip::UIKitClipTest005()
{
if (container_ == nullptr) {
return;
}
CreateTitleLabel("多边形裁剪 ");
ClipPath path;
// {20, 70}, {50, 60}, {110, 80}, {110, 130}, {50, 100}, {20, 120}: path points
path.MoveTo({20, 70}).LineTo({50, 60}).LineTo({110, 80}).LineTo({110, 130}).LineTo({50, 100}).LineTo({20, 120});
UIImageView* imageView = CreateImageView();
ClipImage(imageView, path);
}
};
\ No newline at end of file
/*
* Copyright (c) 2021-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UI_TEST_CLIP_H
#define UI_TEST_CLIP_H
#include "components/ui_image_view.h"
#include "components/ui_label_button.h"
#include "components/ui_scroll_view.h"
#include "draw/clip_utils.h"
#include "ui_test.h"
namespace OHOS {
class UITestClip : public UITest, public UIView::OnClickListener {
public:
UITestClip() {}
~UITestClip() {}
void SetUp() override;
void TearDown() override;
UIView* GetTestView() override;
bool OnClick(UIView& view, const ClickEvent& event) override;
void UIKitClipTest001();
void UIKitClipTest002();
void UIKitClipTest003();
void UIKitClipTest004();
void UIKitClipTest005();
private:
const static int32_t BUFFER_SIZE = 20;
const static int16_t GAP = 10;
const static int16_t TITLE_HEIGHT = 29;
const static uint16_t BLOCK_WIDTH = 454;
const static uint16_t BLOCK_HEIGHT = 200;
const static uint16_t IMAGE_POSITION_X = 20;
const static uint16_t IMAGE_POSITION_Y = 20;
const static int16_t ANGLE_STEP = 5;
const static int16_t RADIUS_STEP_1 = 1;
const static int16_t RADIUS_STEP_5 = 5;
void CreateTitleLabel(const char* title);
UIImageView* CreateImageView();
void ClipImage(UIImageView* imageView, ClipPath& path);
void SetUpButton(UILabelButton* btn, const char* title, int16_t x, int16_t y);
UILabelButton* btnRadiusInc1_ = nullptr;
UILabelButton* btnRadiusInc5_ = nullptr;
UILabelButton* btnRadiusDec1_ = nullptr;
UILabelButton* btnRadiusDec5_ = nullptr;
UILabelButton* btnStartAngleInc_ = nullptr;
UILabelButton* btnStartAngleDec_ = nullptr;
UILabelButton* btnEndAngleInc_ = nullptr;
UILabelButton* btnEndAngleDec_ = nullptr;
UILabel* radiusText_ = nullptr;
UIImageView* imageView1_ = nullptr;
UIImageView* imageView2_ = nullptr;
UIScrollView* container_ = nullptr;
};
} // namespace OHOS
#endif
\ No newline at end of file
......@@ -92,6 +92,7 @@ SOURCES += \
../../../../frameworks/dock/vibrator_manager.cpp \
../../../../frameworks/dock/virtual_input_device.cpp \
../../../../frameworks/engines/gfx/gfx_engine_manager.cpp \
../../../../frameworks/draw/clip_utils.cpp \
../../../../frameworks/draw/draw_arc.cpp \
../../../../frameworks/draw/draw_curve.cpp \
../../../../frameworks/draw/draw_image.cpp \
......@@ -145,6 +146,7 @@ HEADERS += \
../../../../frameworks/dock/input_device.h \
../../../../frameworks/dock/pointer_input_device.h \
../../../../frameworks/dock/virtual_input_device.h \
../../../../frameworks/draw/clip_utils.h \
../../../../frameworks/draw/draw_arc.h \
../../../../frameworks/draw/draw_curve.h \
../../../../frameworks/draw/draw_image.h \
......
......@@ -36,6 +36,7 @@ SOURCES += \
../../../../test/uitest/test_canvas/ui_test_canvas.cpp \
../../../../test/uitest/test_chart/ui_test_chart_pillar.cpp \
../../../../test/uitest/test_chart/ui_test_chart_polyline.cpp \
../../../../test/uitest/test_clip/ui_test_clip.cpp \
../../../../test/uitest/test_dialog/ui_test_dialog.cpp \
../../../../test/uitest/test_digital_clock/ui_test_digital_clock.cpp \
../../../../test/uitest/test_draw_line/ui_test_draw_line.cpp \
......@@ -92,6 +93,7 @@ HEADERS += \
../../../../test/uitest/test_canvas/ui_test_canvas.h \
../../../../test/uitest/test_chart/ui_test_chart_pillar.h \
../../../../test/uitest/test_chart/ui_test_chart_polyline.h \
../../../../test/uitest/test_clip/ui_test_clip.cpp \
../../../../test/uitest/test_dialog/ui_test_dialog.h \
../../../../test/uitest/test_digital_clock/ui_test_digital_clock.h \
../../../../test/uitest/test_draw_line/ui_test_draw_line.h \
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册