1/* 2 * Copyright (c) 2023-2023 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 16import FileModel from '../../Model/FileModel'; 17import { Log, MediaSize } from '@ohos/common'; 18import { Constants, PageDirection, ColorCode } from '@ohos/common'; 19import { GlobalThisHelper, GlobalThisStorageKey } from '@ohos/common'; 20import CheckEmptyUtils from '@ohos/common'; 21import image from '@ohos.multimedia.image'; 22 23const canvasScale = 2 24const to300DPI = 300/72 25const TAG = 'PreviewComponent' 26 27@Component 28export struct PreviewComponent { 29 30 @Consume('ImageCount') imageCount: number 31 @Consume('CurrentIndex') currentIndex: number 32 @Consume('IsGlobalDisable') isGlobalDisable: boolean 33 @Consume('PreviewCompHeight')@Watch('onHeightLoad') previewCompHeight: number//预览区组件高度 34 35 @Consume('IsBorderless')isBorderless: boolean 36 @Consume('PrintRange')@Watch('onPrintRangeChange') printRange: Array<number> 37 @Link @Watch('onPageDirectionChange') pageDirection:number 38 @Link @Watch('onMediaSizeChange') mediaSize:MediaSize 39 @Link @Watch('handleImage') imageSources: Array<FileModel> 40 41 @State currentImage: FileModel = undefined; 42 @State isPreviewFailed: boolean = false; 43 @State @Watch('loadCurrentPix')currentPixelMap: PixelMap = undefined; 44 @State imageOrientation: number = PageDirection.VERTICAL; 45 @State canvasWidth: number = 313 / canvasScale; 46 @State canvasHeight: number = 444 / canvasScale; 47 @State canvasRatio: number = 313 / 444;//width/height 48 @State offCanvasWidth: number = this.canvasWidth * to300DPI * canvasScale; 49 @State offCanvasHeight: number = this.canvasHeight * to300DPI * canvasScale; 50 @State imageWidth: number = Constants.NUMBER_0; 51 @State imageHeight: number = Constants.NUMBER_0; 52 @State xPosition: number = Constants.NUMBER_0; 53 @State yPosition: number = Constants.NUMBER_0; 54 @State firstPageBackgroundColor: Resource = $r('app.color.effect_color_none'); 55 @State previousBackgroundColor: Resource = $r('app.color.effect_color_none'); 56 @State nextBackgroundColor: Resource = $r('app.color.effect_color_none'); 57 @State lastPageBackgroundColor: Resource = $r('app.color.effect_color_none'); 58 @State originalIndex: number = 1; 59 private readonly maxWidth = 304 60 private readonly maxHeight = 261 61 @Link colorMode:number 62 63 private rate: number = 1.5 64 65 build() { 66 Column() { 67 Row() { 68 Image($r('app.media.ic_firstPage')).width(36).height(36).key('PreviewComponent_Image_firstPage') 69 .enabled(this.currentIndex !== 1 && !this.isGlobalDisable) 70 .opacity((this.currentIndex !== 1 && !this.isGlobalDisable) ? Constants.NUMBER_1 : $r('app.float.disable_opacity')) 71 .onClick(() => { 72 this.currentIndex = 1; 73 this.parseImageSize(false); 74 }) 75 .borderRadius($r('app.float.radius_m')) 76 .backgroundColor(this.firstPageBackgroundColor) 77 .onHover((isHover: boolean) => { 78 if (isHover) { 79 this.firstPageBackgroundColor = $r('app.color.effect_color_hover') 80 } else { 81 this.firstPageBackgroundColor = $r('app.color.effect_color_none') 82 } 83 }) 84 .onTouch((event: TouchEvent) => { 85 if (event.type === TouchType.Down) { 86 this.firstPageBackgroundColor = $r('app.color.effect_color_press') 87 } 88 if (event.type === TouchType.Up) { 89 this.firstPageBackgroundColor = $r('app.color.effect_color_none') 90 } 91 92 }) 93 Image($r('app.media.ic_previous')).width(36).height(36).key('PreviewComponent_Image_previous') 94 .enabled(this.currentIndex !== 1 && !this.isGlobalDisable) 95 .opacity((this.currentIndex !== 1 && !this.isGlobalDisable) ? Constants.NUMBER_1 : $r('app.float.disable_opacity')) 96 .onClick(() => { 97 Log.info(TAG,'this.currentIndex --:'+this.currentIndex) 98 if(this.currentIndex === 1) { 99 return 100 } 101 this.currentIndex -= 1; 102 this.parseImageSize(false); 103 }) 104 .borderRadius($r('app.float.radius_m')) 105 .backgroundColor(this.previousBackgroundColor) 106 .onHover((isHover: boolean) => { 107 if (isHover) { 108 this.previousBackgroundColor = $r('app.color.effect_color_hover') 109 } else { 110 this.previousBackgroundColor = $r('app.color.effect_color_none') 111 } 112 }) 113 .onTouch((event: TouchEvent) => { 114 if (event.type === TouchType.Down) { 115 this.previousBackgroundColor = $r('app.color.effect_color_press') 116 } 117 if (event.type === TouchType.Up) { 118 this.previousBackgroundColor = $r('app.color.effect_color_none') 119 } 120 121 }) 122 Text(this.currentIndex+'/'+this.imageCount).key('PreviewComponent_Text_imageCount') 123 .fontSize($r('app.float.font_size_body1')) 124 .width($r('app.float.preview_pages_comp_width')) 125 .height($r('app.float.params_comp_height')).textAlign(TextAlign.Center) 126 .margin({left:$r('app.float.preview_pages_comp_margin_left'),right:$r('app.float.preview_pages_comp_margin_right')}) 127 Image($r('app.media.ic_next')).key('PreviewComponent_Image_next') 128 .width(36) 129 .height(36) 130 .enabled(this.currentIndex !== this.imageCount && !this.isGlobalDisable) 131 .opacity((this.currentIndex !== this.imageCount && !this.isGlobalDisable) ? Constants.NUMBER_1 : $r('app.float.disable_opacity')) 132 .onClick(() => { 133 Log.info(TAG,'this.currentIndex --:'+this.currentIndex) 134 if(this.currentIndex === this.imageCount) { 135 return; 136 } 137 this.currentIndex += 1; 138 this.parseImageSize(false); 139 }) 140 .borderRadius($r('app.float.radius_m')) 141 .backgroundColor(this.nextBackgroundColor) 142 .onHover((isHover: boolean) => { 143 if (isHover) { 144 this.nextBackgroundColor = $r('app.color.effect_color_hover') 145 } else { 146 this.nextBackgroundColor = $r('app.color.effect_color_none') 147 } 148 }) 149 .onTouch((event: TouchEvent) => { 150 if (event.type === TouchType.Down) { 151 this.nextBackgroundColor = $r('app.color.effect_color_press') 152 } 153 if (event.type === TouchType.Up) { 154 this.nextBackgroundColor = $r('app.color.effect_color_none') 155 } 156 157 }) 158 Image($r('app.media.ic_lastPage')).key('PreviewComponent_Image_lastPage') 159 .width(36) 160 .height(36) 161 .enabled(this.currentIndex !== this.imageCount && !this.isGlobalDisable) 162 .opacity((this.currentIndex !== this.imageCount && !this.isGlobalDisable) ? Constants.NUMBER_1 : $r('app.float.disable_opacity')) 163 .onClick(() => { 164 this.currentIndex = this.imageCount; 165 this.parseImageSize(false) 166 }) 167 .borderRadius($r('app.float.radius_m')) 168 .backgroundColor(this.lastPageBackgroundColor) 169 .onHover((isHover: boolean) => { 170 if (isHover) { 171 this.lastPageBackgroundColor = $r('app.color.effect_color_hover') 172 } else { 173 this.lastPageBackgroundColor = $r('app.color.effect_color_none') 174 } 175 }) 176 .onTouch((event: TouchEvent) => { 177 if (event.type === TouchType.Down) { 178 this.lastPageBackgroundColor = $r('app.color.effect_color_press') 179 } 180 if (event.type === TouchType.Up) { 181 this.lastPageBackgroundColor = $r('app.color.effect_color_none') 182 } 183 184 }) 185 }.height(48) 186 .margin({top:8, bottom : 8}) 187 Row() { 188 if (this.currentPixelMap) { 189 Image(this.currentPixelMap).key('PreviewComponent_Image_currentPixelMap') 190 .width(this.canvasWidth).height(this.canvasHeight) 191 .backgroundColor($r('app.color.white')) 192 .objectFit(this.isBorderless?ImageFit.Cover:ImageFit.Contain) 193 .renderMode(this.colorMode === ColorCode.COLOR ? ImageRenderMode.Original : ImageRenderMode.Template) 194 .shadow(ShadowStyle.OUTER_DEFAULT_MD) 195 }else{ 196 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 197 Column(){ 198 if (CheckEmptyUtils.isEmptyArr(this.imageSources)) { 199 Image($r('app.media.ic_gallery_list_damage')).fillColor('#66000000').width(36).height(36).margin({bottom:8}) 200 Text($r('app.string.preview_failed')).fontColor('#66000000').fontSize($r('app.float.font_size_body2')) 201 }else{ 202 LoadingProgress().width(36).height(36).margin({bottom:8}) 203 Text( $r('app.string.preview_loading')).fontColor('#66000000').fontSize($r('app.float.font_size_body2')) 204 } 205 } 206 }.width(this.canvasWidth).height(this.canvasHeight).backgroundColor($r('app.color.white')) 207 .shadow({ radius: $r('app.float.radius_m'), color: Color.Gray, offsetX: Constants.NUMBER_0, offsetY: Constants.NUMBER_0 }) 208 } 209 } 210 .width(480) 211 .height(this.previewCompHeight-80) 212 .justifyContent(FlexAlign.Center) 213 .alignItems(VerticalAlign.Center) 214 .margin({bottom : $r('app.float.preview_comp_margin')}) 215 } 216 .width($r('app.float.preview_comp_width')) 217 .height(this.previewCompHeight) 218 .alignItems(HorizontalAlign.Center) 219 .backgroundColor($r('app.color.preview_background_color')) 220 } 221 222 aboutToAppear() { 223 if(this.imageSources !== undefined){ 224 this.handleImage() 225 }else{ 226 this.checkCanvasWidth() 227 } 228 } 229 230 aboutToDisappear() { 231 } 232 233 parseImageSize(isRendered: boolean) { 234 this.originalIndex = this.printRange[this.currentIndex - 1] 235 this.currentImage = this.imageSources[this.originalIndex - 1]; 236 if (CheckEmptyUtils.isEmpty(this.currentImage)){ 237 return; 238 } 239 if (!isRendered) { 240 this.fdToPixelMap(this.currentImage.fd); 241 } 242 let width = this.currentImage.width 243 let height = this.currentImage.height 244 if(width > height) { 245 this.imageOrientation = PageDirection.LANDSCAPE //图片横向 246 } else { 247 this.imageOrientation = PageDirection.VERTICAL //图片竖向 248 } 249 this.updateCanvasSize() 250 } 251 252 updateCanvasSize() { 253 if (this.pageDirection === PageDirection.AUTO) { 254 if (this.imageOrientation === PageDirection.LANDSCAPE) {//图片横向 255 this.setWidthMax() 256 } else if (this.imageOrientation === PageDirection.VERTICAL) {//图片竖向 257 this.setHeightMax() 258 } 259 } else if (this.pageDirection === PageDirection.LANDSCAPE) { 260 //横向 261 this.setWidthMax() 262 } else if (this.pageDirection === PageDirection.VERTICAL) { 263 //竖向 264 this.setHeightMax() 265 } 266 } 267 268 setWidthMax(){ 269 this.canvasWidth = this.maxWidth 270 this.canvasHeight = this.canvasWidth * this.canvasRatio 271 } 272 273 setHeightMax(){ 274 this.canvasHeight = this.previewCompHeight-80 275 this.canvasWidth = this.canvasHeight * this.canvasRatio 276 } 277 278 279 /** 280 * 纸张尺寸修改事件 281 */ 282 onMediaSizeChange() { 283 let pixelSize = this.mediaSize.getPixelMediaSize() 284 this.canvasWidth = pixelSize.width / this.rate 285 this.canvasHeight = pixelSize.height / this.rate; 286 this.canvasRatio = pixelSize.width / pixelSize.height 287 this.checkCanvasWidth() 288 this.parseImageSize(true) 289 } 290 291 /** 292 * 纸张方向修改 293 */ 294 onPageDirectionChange() { 295 Log.info(TAG, 'onPageDirectionChange enter'); 296 this.parseImageSize(true) 297 } 298 /** 299 * 打印范围修改 300 */ 301 onPrintRangeChange() { 302 Log.info(TAG, 'onPrintRangeChange enter',JSON.stringify(this.printRange)); 303 if (this.printRange.length === 0 || this.imageSources === undefined) { 304 return; 305 } 306 if (this.currentIndex>this.printRange.length) { 307 Log.info(TAG, 'parseImageSize this.printRange.length: ', JSON.stringify(this.printRange.length)) 308 this.currentIndex = this.printRange.length 309 } 310 let newIndex = this.printRange[this.currentIndex - 1] 311 Log.info(TAG,'newIndex: '+newIndex+' originalIndex: '+this.originalIndex) 312 if (newIndex === this.originalIndex) { 313 this.parseImageSize(true) 314 } else { 315 this.parseImageSize(false) 316 } 317 } 318 319 /** 320 *界面高度调整 321 */ 322 onHeightLoad(){ 323 Log.info(TAG,'onHeightLoad:'+this.previewCompHeight) 324 if (this.imageSources === undefined ){ 325 return; 326 } 327 if(this.previewCompHeight){ 328 this.handleImage(); 329 } 330 } 331 332 333 /** 334 * 调整画布的方向 335 */ 336 swapCanvasWidthAndHeight() { 337 let temp = this.canvasWidth; 338 this.canvasWidth = this.canvasHeight 339 this.canvasHeight = temp 340 this.checkCanvasWidth() 341 } 342 /** 343 * 画布宽度是否超过最大值 344 */ 345 checkCanvasWidth(){ 346 let ratio = null 347 if(this.canvasWidth > Constants.CANVAS_MAX_WIDTH){ 348 ratio = this.canvasHeight/this.canvasWidth 349 this.canvasWidth = Constants.CANVAS_MAX_WIDTH 350 this.canvasHeight = this.canvasWidth * ratio 351 } 352 if(this.canvasHeight > Constants.CANVAS_MAX_HEIGHT){ 353 ratio = this.canvasWidth/this.canvasHeight 354 this.canvasHeight = Constants.CANVAS_MAX_HEIGHT 355 this.canvasWidth = this.canvasHeight * ratio 356 } 357 } 358 /** 359 * fd生成pixelMap 360 * @param fd 361 */ 362 fdToPixelMap(fd:number){ 363 if (image === undefined) { 364 Log.error(TAG, 'image is undefined'); 365 return; 366 } 367 Log.info(TAG, 'fd is: ' + fd); 368 Log.debug(TAG, 'this.currentImage: ' + JSON.stringify(this.currentImage)); 369 if (this.currentImage === undefined) { 370 Log.error(TAG, 'currentImage is undefined'); 371 return; 372 } 373 if (CheckEmptyUtils.isEmpty(this.currentImage.imageSource)) { 374 this.currentImage.imageSource = image.createImageSource(fd); 375 if (this.currentImage.imageSource === undefined) { 376 Log.error(TAG, 'createImageSource error'); 377 return; 378 } 379 } 380 let info = this.currentImage.imageSource.getImageInfo(); 381 if (CheckEmptyUtils.isEmpty(info)) { 382 Log.error(TAG, 'createImageSource error: invalid imageSource'); 383 return; 384 } 385 let decodingOptions 386 let width = this.currentImage.width 387 let height = this.currentImage.height 388 let ratio = width/height 389 if(width*height>=Constants.MAX_PIXELMAP){//超过像素上限pixelmap无法显示在image组件里,需要resize 390 height = Math.floor(Math.sqrt(Constants.MAX_PIXELMAP/ratio)) 391 Log.info(TAG,'decodingOptions width: '+height*ratio+' height: '+height) 392 decodingOptions = { 393 desiredSize:{width:height*ratio,height:height} 394 } 395 } 396 this.currentImage.imageSource.createPixelMap(decodingOptions).then(pixelmap => { 397 Log.info(TAG,'Succeeded in creating pixelmap object through image decoding parameters.'); 398 if(this.currentPixelMap!==undefined){ 399 this.currentPixelMap.release().then(()=>{ 400 Log.info(TAG,'currentPixelMap release success'); 401 this.currentPixelMap = pixelmap 402 GlobalThisHelper.createValue<PixelMap>(this.currentPixelMap, GlobalThisStorageKey.KEY_CURRENT_PIXELMAP); 403 }) 404 }else{ 405 this.currentPixelMap = pixelmap 406 GlobalThisHelper.createValue<PixelMap>(this.currentPixelMap, GlobalThisStorageKey.KEY_CURRENT_PIXELMAP); 407 } 408 409 }).catch(error => { 410 Log.info(TAG,'Failed to create pixelmap object through image decoding parameters.'+error); 411 }) 412 } 413 414 handleImage(){ 415 Log.info(TAG,'handleImage'+this.imageSources.length) 416 this.checkCanvasWidth() 417 this.parseImageSize(false); 418 } 419 420 loadCurrentPix(){ 421 if (this.currentPixelMap === undefined) { 422 Log.info(TAG,'loadCurrentPix load failed') 423 }else{ 424 Log.info(TAG,'loadCurrentPix load success') 425 } 426 } 427} 428