1 /*
2 * Copyright (c) 2024 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 "font_napi/js_typeface.h"
17 #include "js_drawing_utils.h"
18
19 #ifdef ROSEN_OHOS
20 #include <parameters.h>
21 #endif
22
23 #include "draw/color.h"
24 #include "effect/color_space.h"
25 #include "image/image_info.h"
26 #include "image/bitmap.h"
27 #include "image/image.h"
28 #include "rosen_text/font_collection.h"
29 #include "txt/platform.h"
30
31 namespace OHOS::Rosen {
32
33 #ifdef ROSEN_OHOS
34 bool JsDrawingTestUtils::closeDrawingTest_ =
35 std::atoi((OHOS::system::GetParameter("persist.sys.graphic.drawing.test", "0").c_str())) != 1;
36 #else
37 bool JsDrawingTestUtils::closeDrawingTest_ = true;
38 #endif
39
40 namespace Drawing {
41 const char* const JSCOLOR[4] = {"alpha", "red", "green", "blue"};
42
BindNativeFunction(napi_env env,napi_value object,const char * name,const char * moduleName,napi_callback func)43 void BindNativeFunction(napi_env env, napi_value object, const char* name, const char* moduleName, napi_callback func)
44 {
45 std::string fullName;
46 if (moduleName) {
47 fullName = moduleName;
48 fullName += '.';
49 }
50 fullName += name;
51 napi_value funcValue = nullptr;
52 napi_create_function(env, fullName.c_str(), fullName.size(), func, nullptr, &funcValue);
53 napi_set_named_property(env, object, fullName.c_str(), funcValue);
54 }
55
CreateJsError(napi_env env,int32_t errCode,const std::string & message)56 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message)
57 {
58 napi_value result = nullptr;
59 napi_create_error(env, CreateJsValue(env, errCode), CreateJsValue(env, message), &result);
60 return result;
61 }
62
ConvertFromJsTextEncoding(napi_env env,TextEncoding & textEncoding,napi_value nativeType)63 bool ConvertFromJsTextEncoding(napi_env env, TextEncoding& textEncoding, napi_value nativeType)
64 {
65 napi_value type = nativeType;
66 if (type == nullptr) {
67 return false;
68 }
69 uint32_t resultValue = 0;
70 napi_get_value_uint32(env, type, &resultValue);
71
72 switch (resultValue) {
73 case 0: // 0: TextEncoding::UTF8
74 textEncoding = TextEncoding::UTF8;
75 break;
76 case 1: // 1: TextEncoding::UTF16
77 textEncoding = TextEncoding::UTF16;
78 break;
79 case 2: // 2: TextEncoding::UTF32
80 textEncoding = TextEncoding::UTF32;
81 break;
82 case 3: // 3: TextEncoding::GLYPH_ID
83 textEncoding = TextEncoding::GLYPH_ID;
84 break;
85 default: // default: TextEncoding::UTF8
86 textEncoding = TextEncoding::UTF8;
87 break;
88 }
89 return true;
90 }
91
NapiThrowError(napi_env env,DrawingErrorCode err,const std::string & message)92 napi_value NapiThrowError(napi_env env, DrawingErrorCode err, const std::string& message)
93 {
94 napi_throw(env, CreateJsError(env, static_cast<int32_t>(err), message));
95 return nullptr;
96 }
97
98 static const char* g_argbString[4] = {"alpha", "red", "green", "blue"};
99 static const char* g_ltrbString[4] = {"left", "top", "right", "bottom"};
100 static const char* g_pointString[2] = {"x", "y"};
101
ConvertFromJsColor(napi_env env,napi_value jsValue,int32_t * argb,size_t size)102 bool ConvertFromJsColor(napi_env env, napi_value jsValue, int32_t* argb, size_t size)
103 {
104 napi_value tempValue = nullptr;
105 for (size_t idx = 0; idx < size; idx++) {
106 int32_t* curChannel = argb + idx;
107 napi_get_named_property(env, jsValue, g_argbString[idx], &tempValue);
108 if (napi_get_value_int32(env, tempValue, curChannel) != napi_ok ||
109 *curChannel < 0 || *curChannel > Color::RGB_MAX) {
110 return false;
111 }
112 }
113 return true;
114 }
115
ConvertFromJsColor4F(napi_env env,napi_value jsValue,double * argbF,size_t size)116 bool ConvertFromJsColor4F(napi_env env, napi_value jsValue, double* argbF, size_t size)
117 {
118 napi_value tempValue = nullptr;
119 for (size_t idx = 0; idx < size; idx++) {
120 double* curChannel = argbF + idx;
121 napi_get_named_property(env, jsValue, g_argbString[idx], &tempValue);
122 if (napi_get_value_double(env, tempValue, curChannel) != napi_ok) {
123 return false;
124 }
125 }
126 return true;
127 }
128
ConvertFromAdaptHexJsColor(napi_env env,napi_value jsValue,Drawing::ColorQuad & jsColor)129 bool ConvertFromAdaptHexJsColor(napi_env env, napi_value jsValue, Drawing::ColorQuad& jsColor)
130 {
131 bool isJsColor = false;
132 napi_has_named_property(env, jsValue, JSCOLOR[0], &isJsColor);
133 if (isJsColor) {
134 int32_t argb[ARGC_FOUR] = {0};
135 if (!ConvertFromJsColor(env, jsValue, argb, ARGC_FOUR)) {
136 return false;
137 }
138 jsColor = Color::ColorQuadSetARGB(argb[ARGC_ZERO], argb[ARGC_ONE], argb[ARGC_TWO], argb[ARGC_THREE]);
139 } else {
140 if (napi_get_value_uint32(env, jsValue, &jsColor) != napi_ok) {
141 return false;
142 }
143 }
144 return true;
145 }
146
ConvertFromAdaptJsColor4F(napi_env env,napi_value jsValue,Drawing::Color4f & jsColor4F)147 bool ConvertFromAdaptJsColor4F(napi_env env, napi_value jsValue, Drawing::Color4f& jsColor4F)
148 {
149 bool isJsColor4F = false;
150 napi_has_named_property(env, jsValue, JSCOLOR[0], &isJsColor4F);
151 if (!isJsColor4F) {
152 return false;
153 }
154
155 double argbF[ARGC_FOUR] = {0};
156 if (!ConvertFromJsColor4F(env, jsValue, argbF, ARGC_FOUR)) {
157 return false;
158 }
159 jsColor4F.alphaF_ = static_cast<float>(argbF[ARGC_ZERO]);
160 jsColor4F.redF_ = static_cast<float>(argbF[ARGC_ONE]);
161 jsColor4F.greenF_ = static_cast<float>(argbF[ARGC_TWO]);
162 jsColor4F.blueF_ = static_cast<float>(argbF[ARGC_THREE]);
163
164 return true;
165 }
166
ConvertFromJsRect(napi_env env,napi_value jsValue,double * ltrb,size_t size)167 bool ConvertFromJsRect(napi_env env, napi_value jsValue, double* ltrb, size_t size)
168 {
169 napi_value tempValue = nullptr;
170 for (size_t idx = 0; idx < size; idx++) {
171 double* curEdge = ltrb + idx;
172 napi_get_named_property(env, jsValue, g_ltrbString[idx], &tempValue);
173 if (napi_get_value_double(env, tempValue, curEdge) != napi_ok) {
174 return false;
175 }
176 }
177 return true;
178 }
179
ConvertFromJsIRect(napi_env env,napi_value jsValue,int32_t * ltrb,size_t size)180 bool ConvertFromJsIRect(napi_env env, napi_value jsValue, int32_t* ltrb, size_t size)
181 {
182 napi_value tempValue = nullptr;
183 for (size_t idx = 0; idx < size; idx++) {
184 int32_t* curEdge = ltrb + idx;
185 napi_get_named_property(env, jsValue, g_ltrbString[idx], &tempValue);
186 if (napi_get_value_int32(env, tempValue, curEdge) != napi_ok) {
187 return false;
188 }
189 }
190 return true;
191 }
192
ConvertFromJsShadowFlag(napi_env env,napi_value src,ShadowFlags & shadowFlag,ShadowFlags defaultFlag)193 bool ConvertFromJsShadowFlag(napi_env env, napi_value src, ShadowFlags& shadowFlag, ShadowFlags defaultFlag)
194 {
195 if (src == nullptr) {
196 return false;
197 }
198 uint32_t value = 0;
199 if (!ConvertFromJsValue(env, src, value)) {
200 return false;
201 }
202 shadowFlag = defaultFlag;
203 if (value >= static_cast<uint32_t>(ShadowFlags::NONE) && value <= static_cast<uint32_t>(ShadowFlags::ALL)) {
204 shadowFlag = static_cast<ShadowFlags>(value);
205 }
206 return true;
207 }
208
ConvertFromJsPoint3d(napi_env env,napi_value src,Point3 & point3d)209 bool ConvertFromJsPoint3d(napi_env env, napi_value src, Point3& point3d)
210 {
211 if (src == nullptr) {
212 return false;
213 }
214 napi_value tempValue = nullptr;
215 double x = 0.0;
216 double y = 0.0;
217 double z = 0.0;
218 napi_get_named_property(env, src, "x", &tempValue);
219 bool isXOk = ConvertFromJsValue(env, tempValue, x);
220 napi_get_named_property(env, src, "y", &tempValue);
221 bool isYOk = ConvertFromJsValue(env, tempValue, y);
222 napi_get_named_property(env, src, "z", &tempValue);
223 bool isZOk = ConvertFromJsValue(env, tempValue, z);
224 if (!(isXOk && isYOk && isZOk)) {
225 return false;
226 }
227 point3d = Point3(x, y, z);
228 return true;
229 }
230
ConvertFromJsPoint(napi_env env,napi_value jsValue,double * point,size_t size)231 bool ConvertFromJsPoint(napi_env env, napi_value jsValue, double* point, size_t size)
232 {
233 if (point == nullptr) {
234 return false;
235 }
236 napi_value tempValue = nullptr;
237 for (size_t idx = 0; idx < size; idx++) {
238 double* curEdge = point + idx;
239 napi_get_named_property(env, jsValue, g_pointString[idx], &tempValue);
240 if (napi_get_value_double(env, tempValue, curEdge) != napi_ok) {
241 return false;
242 }
243 }
244 return true;
245 }
246
ConvertFromJsPointsArray(napi_env env,napi_value array,Drawing::Point * points,uint32_t count)247 bool ConvertFromJsPointsArray(napi_env env, napi_value array, Drawing::Point* points, uint32_t count)
248 {
249 if (points == nullptr) {
250 return false;
251 }
252 for (uint32_t i = 0; i < count; i++) {
253 napi_value tempPoint = nullptr;
254 if (napi_get_element(env, array, i, &tempPoint) != napi_ok) {
255 return false;
256 }
257 if (!GetDrawingPointFromJsValue(env, tempPoint, points[i])) {
258 return false;
259 }
260 }
261 return true;
262 }
263
ConvertFromJsPointsArrayOffset(napi_env env,napi_value array,Drawing::Point * points,uint32_t count,uint32_t offset)264 bool ConvertFromJsPointsArrayOffset(napi_env env, napi_value array, Drawing::Point* points,
265 uint32_t count, uint32_t offset)
266 {
267 if (points == nullptr) {
268 return false;
269 }
270 for (uint32_t i = offset; i < offset + count; i++) {
271 napi_value tempPoint = nullptr;
272 if (napi_get_element(env, array, i, &tempPoint) != napi_ok) {
273 return false;
274 }
275 if (!GetDrawingPointFromJsValue(env, tempPoint, points[i - offset])) {
276 return false;
277 }
278 }
279 return true;
280 }
281
GetFontMetricsAndConvertToJsValue(napi_env env,FontMetrics * metrics)282 napi_value GetFontMetricsAndConvertToJsValue(napi_env env, FontMetrics* metrics)
283 {
284 napi_value objValue = nullptr;
285 napi_create_object(env, &objValue);
286 if (metrics != nullptr && objValue != nullptr) {
287 napi_set_named_property(env, objValue, "top", CreateJsNumber(env, metrics->fTop));
288 napi_set_named_property(env, objValue, "ascent", CreateJsNumber(env, metrics->fAscent));
289 napi_set_named_property(env, objValue, "descent", CreateJsNumber(env, metrics->fDescent));
290 napi_set_named_property(env, objValue, "bottom", CreateJsNumber(env, metrics->fBottom));
291 napi_set_named_property(env, objValue, "leading", CreateJsNumber(env, metrics->fLeading));
292 napi_set_named_property(env, objValue, "flags", CreateJsNumber(env, metrics->fFlags));
293 napi_set_named_property(env, objValue, "avgCharWidth", CreateJsNumber(env, metrics->fAvgCharWidth));
294 napi_set_named_property(env, objValue, "maxCharWidth", CreateJsNumber(env, metrics->fMaxCharWidth));
295 napi_set_named_property(env, objValue, "xMin", CreateJsNumber(env, metrics->fXMin));
296 napi_set_named_property(env, objValue, "xMax", CreateJsNumber(env, metrics->fXMax));
297 napi_set_named_property(env, objValue, "xHeight", CreateJsNumber(env, metrics->fXHeight));
298 napi_set_named_property(env, objValue, "capHeight", CreateJsNumber(env, metrics->fCapHeight));
299 napi_set_named_property(env, objValue, "underlineThickness", CreateJsNumber(env,
300 metrics->fUnderlineThickness));
301 napi_set_named_property(env, objValue, "underlinePosition", CreateJsNumber(env,
302 metrics->fUnderlinePosition));
303 napi_set_named_property(env, objValue, "strikethroughThickness", CreateJsNumber(env,
304 metrics->fStrikeoutThickness));
305 napi_set_named_property(env, objValue, "strikethroughPosition", CreateJsNumber(env,
306 metrics->fStrikeoutPosition));
307 }
308 return objValue;
309 }
310
311 #if defined(ROSEN_OHOS) || defined(ROSEN_ARKUI_X)
ColorSpaceToDrawingColorSpace(Media::ColorSpace colorSpace)312 std::shared_ptr<Drawing::ColorSpace> ColorSpaceToDrawingColorSpace(Media::ColorSpace colorSpace)
313 {
314 switch (colorSpace) {
315 case Media::ColorSpace::DISPLAY_P3:
316 return Drawing::ColorSpace::CreateRGB(Drawing::CMSTransferFuncType::SRGB, Drawing::CMSMatrixType::DCIP3);
317 case Media::ColorSpace::LINEAR_SRGB:
318 return Drawing::ColorSpace::CreateSRGBLinear();
319 case Media::ColorSpace::SRGB:
320 return Drawing::ColorSpace::CreateSRGB();
321 default:
322 return Drawing::ColorSpace::CreateSRGB();
323 }
324 }
325
PixelFormatToDrawingColorType(Media::PixelFormat pixelFormat)326 Drawing::ColorType PixelFormatToDrawingColorType(Media::PixelFormat pixelFormat)
327 {
328 switch (pixelFormat) {
329 case Media::PixelFormat::RGB_565:
330 return Drawing::ColorType::COLORTYPE_RGB_565;
331 case Media::PixelFormat::RGBA_8888:
332 return Drawing::ColorType::COLORTYPE_RGBA_8888;
333 case Media::PixelFormat::BGRA_8888:
334 return Drawing::ColorType::COLORTYPE_BGRA_8888;
335 case Media::PixelFormat::ALPHA_8:
336 return Drawing::ColorType::COLORTYPE_ALPHA_8;
337 case Media::PixelFormat::RGBA_F16:
338 return Drawing::ColorType::COLORTYPE_RGBA_F16;
339 case Media::PixelFormat::UNKNOWN:
340 case Media::PixelFormat::ARGB_8888:
341 case Media::PixelFormat::RGB_888:
342 case Media::PixelFormat::NV21:
343 case Media::PixelFormat::NV12:
344 case Media::PixelFormat::CMYK:
345 default:
346 return Drawing::ColorType::COLORTYPE_UNKNOWN;
347 }
348 }
349
AlphaTypeToDrawingAlphaType(Media::AlphaType alphaType)350 Drawing::AlphaType AlphaTypeToDrawingAlphaType(Media::AlphaType alphaType)
351 {
352 switch (alphaType) {
353 case Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
354 return Drawing::AlphaType::ALPHATYPE_UNKNOWN;
355 case Media::AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
356 return Drawing::AlphaType::ALPHATYPE_OPAQUE;
357 case Media::AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
358 return Drawing::AlphaType::ALPHATYPE_PREMUL;
359 case Media::AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
360 return Drawing::AlphaType::ALPHATYPE_UNPREMUL;
361 default:
362 return Drawing::AlphaType::ALPHATYPE_UNKNOWN;
363 }
364 }
365
ExtracetDrawingBitmap(std::shared_ptr<Media::PixelMap> pixelMap,Drawing::Bitmap & bitmap)366 bool ExtracetDrawingBitmap(std::shared_ptr<Media::PixelMap> pixelMap, Drawing::Bitmap& bitmap)
367 {
368 if (!pixelMap) {
369 ROSEN_LOGE("Drawing_napi ::pixelMap fail");
370 return false;
371 }
372 Media::ImageInfo imageInfo;
373 pixelMap->GetImageInfo(imageInfo);
374 Drawing::ImageInfo drawingImageInfo { imageInfo.size.width, imageInfo.size.height,
375 PixelFormatToDrawingColorType(imageInfo.pixelFormat), AlphaTypeToDrawingAlphaType(imageInfo.alphaType),
376 ColorSpaceToDrawingColorSpace(imageInfo.colorSpace) };
377 bitmap.Build(drawingImageInfo, pixelMap->GetRowStride());
378 bitmap.SetPixels(const_cast<void*>(reinterpret_cast<const void*>(pixelMap->GetPixels())));
379 return true;
380 }
381
382 struct PixelMapReleaseContext {
PixelMapReleaseContextOHOS::Rosen::Drawing::PixelMapReleaseContext383 explicit PixelMapReleaseContext(std::shared_ptr<Media::PixelMap> pixelMap) : pixelMap_(pixelMap) {}
384
~PixelMapReleaseContextOHOS::Rosen::Drawing::PixelMapReleaseContext385 ~PixelMapReleaseContext()
386 {
387 pixelMap_ = nullptr;
388 }
389
390 private:
391 std::shared_ptr<Media::PixelMap> pixelMap_;
392 };
393
PixelMapReleaseProc(const void *,void * context)394 static void PixelMapReleaseProc(const void* /* pixels */, void* context)
395 {
396 PixelMapReleaseContext* ctx = static_cast<PixelMapReleaseContext*>(context);
397 if (ctx) {
398 delete ctx;
399 ctx = nullptr;
400 }
401 }
402
ExtractDrawingImage(std::shared_ptr<Media::PixelMap> pixelMap)403 std::shared_ptr<Drawing::Image> ExtractDrawingImage(std::shared_ptr<Media::PixelMap> pixelMap)
404 {
405 if (!pixelMap) {
406 ROSEN_LOGE("Drawing_napi::pixelMap fail");
407 return nullptr;
408 }
409 Media::ImageInfo imageInfo;
410 pixelMap->GetImageInfo(imageInfo);
411 Drawing::ImageInfo drawingImageInfo { imageInfo.size.width, imageInfo.size.height,
412 PixelFormatToDrawingColorType(imageInfo.pixelFormat), AlphaTypeToDrawingAlphaType(imageInfo.alphaType),
413 ColorSpaceToDrawingColorSpace(imageInfo.colorSpace) };
414 Drawing::Pixmap imagePixmap(
415 drawingImageInfo, reinterpret_cast<const void*>(pixelMap->GetPixels()), pixelMap->GetRowStride());
416 PixelMapReleaseContext* releaseContext = new PixelMapReleaseContext(pixelMap);
417 auto image = Drawing::Image::MakeFromRaster(imagePixmap, PixelMapReleaseProc, releaseContext);
418 if (!image) {
419 ROSEN_LOGE("Drawing_napi :RSPixelMapUtil::ExtractDrawingImage fail");
420 delete releaseContext;
421 releaseContext = nullptr;
422 }
423 return image;
424 }
425 #endif
426
GetThemeFont(std::shared_ptr<Font> font)427 std::shared_ptr<Font> GetThemeFont(std::shared_ptr<Font> font)
428 {
429 std::shared_ptr<FontMgr> fontMgr = GetFontMgr(font);
430 if (fontMgr == nullptr) {
431 return nullptr;
432 }
433 std::shared_ptr<Typeface> themeTypeface =
434 std::shared_ptr<Typeface>(fontMgr->MatchFamilyStyle(SPText::OHOS_THEME_FONT, FontStyle()));
435 if (themeTypeface == nullptr) {
436 return nullptr;
437 }
438 std::shared_ptr<Font> themeFont = std::make_shared<Font>(*font);
439 themeFont->SetTypeface(themeTypeface);
440 return themeFont;
441 }
442
MatchThemeFont(std::shared_ptr<Font> font,int32_t unicode)443 std::shared_ptr<Font> MatchThemeFont(std::shared_ptr<Font> font, int32_t unicode)
444 {
445 std::shared_ptr<FontMgr> fontMgr = GetFontMgr(font);
446 if (fontMgr == nullptr) {
447 return nullptr;
448 }
449 auto themeFamilies = SPText::DefaultFamilyNameMgr::GetInstance().GetThemeFontFamilies();
450 std::shared_ptr<Drawing::Font> themeFont = std::make_shared<Drawing::Font>(*font);
451 for (const auto& family : themeFamilies) {
452 std::shared_ptr<Drawing::Typeface> themeTypeface =
453 std::shared_ptr<Drawing::Typeface>(fontMgr->MatchFamilyStyle(family.c_str(), FontStyle()));
454 themeFont->SetTypeface(themeTypeface);
455 if (themeFont->UnicharToGlyph(unicode)) {
456 return themeFont;
457 }
458 }
459 return nullptr;
460 }
461
GetFontMgr(std::shared_ptr<Font> font)462 std::shared_ptr<FontMgr> GetFontMgr(std::shared_ptr<Font> font)
463 {
464 if (!font->IsThemeFontFollowed() || font->GetTypeface() != JsTypeface::GetZhCnTypeface()) {
465 return nullptr;
466 }
467
468 std::shared_ptr<FontCollection> fontCollection = FontCollection::Create();
469 if (fontCollection == nullptr) {
470 return nullptr;
471 }
472 std::shared_ptr<FontMgr> fontMgr = fontCollection->GetFontMgr();
473 if (fontMgr == nullptr) {
474 return nullptr;
475 }
476 return fontMgr;
477 }
478
MakeFontFeaturesFromJsArray(napi_env env,std::shared_ptr<DrawingFontFeatures> features,uint32_t size,napi_value & array)479 void MakeFontFeaturesFromJsArray(napi_env env, std::shared_ptr<DrawingFontFeatures> features,
480 uint32_t size, napi_value& array)
481 {
482 features->clear();
483
484 for (uint32_t i = 0; i < size; ++i) {
485 napi_value tempNumber = nullptr;
486 napi_status status = napi_get_element(env, array, i, &tempNumber);
487 if (status != napi_ok || tempNumber == nullptr) {
488 continue;
489 }
490 std::string name;
491 napi_value tempValue = nullptr;
492 status = napi_get_named_property(env, tempNumber, "name", &tempValue);
493 if (status != napi_ok || tempValue == nullptr) {
494 continue;
495 }
496 if (!ConvertFromJsValue(env, tempValue, name)) {
497 continue;
498 }
499 double value = 0.0;
500 status = napi_get_named_property(env, tempNumber, "value", &tempValue);
501 if (status != napi_ok || tempValue == nullptr) {
502 continue;
503 }
504 if (!ConvertFromJsValue(env, tempValue, value)) {
505 continue;
506 }
507 features->push_back({{name, value}});
508 }
509 }
510 } // namespace Drawing
511 } // namespace OHOS::Rosen
512