Sfoglia il codice sorgente

add: 属性面板 - 旋转、翻转、替换、预览图片,设置透明度;

wzl 1 anno fa
parent
commit
b56b72764c

+ 159 - 321
packages/core/src/editor/image_editor.js

@@ -40,6 +40,7 @@ export class ImageEditor {
     this.state = 0 // 0 未选中;1 已选中;2 裁剪状态
     this.mouseDown = false
     this.ratio = window.devicePixelRatio || 1
+    this.opacity = 1
 
     this.start = null
     this.end = null
@@ -61,11 +62,9 @@ export class ImageEditor {
     this.onMousedown = this.handleMouseDown.bind(this)
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
-    // this.onKeydown = this.handleKeyDown.bind(this)
+    this.onKeydown = this.handleKeyDown.bind(this)
     // this.onTextInput = this.handleTextInput.bind(this)
     this.onHandlePropertyPanelChanged = this.handlePropertyPanelChanged.bind(this)
-    // this.onCompositionstart = this.handleCompositionstart.bind(this)
-    // this.onCompositionend = this.handleCompositionend.bind(this)
 
     this.render()
   }
@@ -74,6 +73,7 @@ export class ImageEditor {
     this.eventBus._on('imagePropertyChanged', this.onHandlePropertyPanelChanged)
 
     await this.updateCanvas()
+    this.opacity = await this.messageHandler.sendWithPromise('GetImageTransparency', this.editAreaPtr)
 
     let frameContainer = createElement(
       'div',
@@ -268,7 +268,7 @@ export class ImageEditor {
     this.outerLine.addEventListener(this.mouseup, this.onMouseup)
 
     if (this.newAdd) {
-      // this.goEditing()
+      this.goEditing()
     }
   }
 
@@ -487,48 +487,10 @@ export class ImageEditor {
       if (!this.startPoint) {
         this.startPoint = endPoint
       }
-      const { start, end } = await this.getCharsRange(this.startPoint, endPoint)
-      this.activeCharPlace = end
-
-      if (!this.cursor) {
-        const cursor = createSvg(
-          'svg',
-          {
-            class: 'cursor-animation'
-          },
-          {
-            position: 'absolute',
-            left: 0,
-            top: 0,
-            width: '100%',
-            height: '100%',
-          }
-        )
-        this.cursor = cursor
-        this.imageContainer.append(this.cursor)
-      }
-      // this.updateCursorLine()
-
-      if ((this.selectedRects && !this.mouseMoved) ||
-        (start.SectionIndex === end.SectionIndex &&
-        start.LineIndex === end.LineIndex &&
-        start.RunIndex === end.RunIndex &&
-        start.CharIndex === end.CharIndex)
-      ) {
-        this.clearSelectText()
-      }
-
-      if (!this.selectedRects) {
-        this.getTextStyle()
-      }
 
       // this.textarea.focus()
       // this.textarea.addEventListener('keydown', this.onKeydown)
       // this.textarea.addEventListener(this.textInput, this.onTextInput)
-      // if (this.isFirefox) {
-      //   this.textarea.addEventListener('compositionstart', this.onCompositionstart)
-      //   this.textarea.addEventListener('compositionend', this.onCompositionend)
-      // }
     }
 
     this.frameContainer.classList.add('selected')
@@ -678,41 +640,17 @@ export class ImageEditor {
         });
       }
     }
-    // else {
-    //   if (!this.startPoint) return
-
-    //   let endPoint
-    //   if (isMobileDevice) {
-    //     const offsetX = e.changedTouches[0].clientX - this.pageViewer.div.getBoundingClientRect().left
-    //     const offsetY = e.changedTouches[0].clientY - this.pageViewer.div.getBoundingClientRect().top
-
-    //     endPoint = {
-    //       x: offsetX / this.scale,
-    //       y: offsetY / this.scale
-    //     }
-    //   } else {
-    //     const offsetX = e.offsetX
-    //     const offsetY = e.offsetY
-
-    //     endPoint = {
-    //       x: (offsetX + this.rect.left) / this.scale,
-    //       y: (offsetY + this.rect.top) / this.scale
-    //     }
-    //   }
-    //   this.endPoint = endPoint
-    //   const { start, end } = await this.getCharsRange(this.startPoint, endPoint)
-    //   this.activeCharPlace = end
-    //   this.selectedCharRange = { start, end }
-
-    //   this.cursor.style.display = 'none'
-    //   this.updateSelectedRect(true)
-    // }
 
     this.mouseMoved = true
   }
 
-  handleClick () {
-    if (!isMobileDevice) this.eventBus.dispatch('contentPropertyChange', { type: 'image', isOpen: true })
+  async handleClick () {
+    if (this.contentContainer.selectedFrameIndex !== -1 && this.contentContainer.selectedFrameIndex !== this.editAreaIndex && this.state === 0) return
+
+    if (!isMobileDevice) {
+      this.updateOverview()
+      this.eventBus.dispatch('contentPropertyChange', { type: 'image', isOpen: true, opacity: this.opacity * 100 })
+    }
   }
 
   async handleOutside () {
@@ -753,10 +691,6 @@ export class ImageEditor {
 
     // this.textarea.removeEventListener('keydown', this.onKeydown)
     // this.textarea.removeEventListener(this.textInput, this.onTextInput)
-    // if (this.isFirefox) {
-    //   this.textarea.removeEventListener('compositionstart', this.onCompositionstart)
-    //   this.textarea.removeEventListener('compositionend', this.onCompositionend)
-    // }
   }
 
   async handleKeyDown (e) {
@@ -800,37 +734,6 @@ export class ImageEditor {
       await this.updateCanvas(null, oldRect)
       this.updateCursorLine()
     }
-
-    this.isPasteKey = (e.metaKey || e.ctrlKey) && (e.key === 'v' || e.key === 'V')
-
-    if (keyCode === 37) { // 左键
-      const newChar = await this.getCharPlace('GetPrevCharPlace')
-      this.activeCharPlace = newChar.start
-    } else if (keyCode === 38) { // 上键
-      const newChar = await this.getCharPlace('GetUpCharPlace')
-      this.activeCharPlace = newChar.start
-    } else if (keyCode === 39) { // 右键
-      const newChar = await this.getCharPlace('GetNextCharPlace')
-      this.activeCharPlace = newChar.start
-    } else if (keyCode === 40) { // 下键
-      const newChar = await this.getCharPlace('GetDownCharPlace')
-      this.activeCharPlace = newChar.start
-    } else if ((e.metaKey || e.ctrlKey) && (e.key === 'c' || e.key === 'C') && this.selectedCharRange) { // 复制
-      const copyText = await this.getText()
-      copy(copyText)
-      return
-    } else if ((e.metaKey || e.ctrlKey) && (e.key === 'x' || e.key === 'X') && this.selectedCharRange) { // 剪切
-      const copyText = await this.getText()
-      copy(copyText)
-
-      const newChar = await this.getCharPlace('DeleteChars')
-      this.activeCharPlace = newChar
-      this.selectedCharRange = null
-    } else {
-      return
-    }
-    this.clearSelectText()
-    this.updateCursorLine()
   }
 
   async handleTextInput (e) {
@@ -855,7 +758,6 @@ export class ImageEditor {
 
     this.saveEdit()
     this.updateCanvas()
-    this.updateCursorLine()
   }
 
   // 获取rect
@@ -864,7 +766,7 @@ export class ImageEditor {
       pagePtr: this.pagePtr,
       editAreaPtr: this.editAreaPtr
     })
-    if (this.newAdd) console.log(this.frame)
+
     const { start, end } = this.getActualRect(
       this.viewport,
       this.scale,
@@ -1007,56 +909,6 @@ export class ImageEditor {
     this.cursor.append(cursorLine)
   }
 
-  // 获取图片属性
-  async getTextStyle () {
-    const textStyle = await this.messageHandler.sendWithPromise('GetTextStyle', {
-      editAreaPtr: this.editAreaPtr,
-      char: this.activeCharPlace
-    })
-    
-    const style = {
-      color: `rgb(${textStyle.R}, ${textStyle.G}, ${textStyle.B})`,
-      opacity: textStyle.Transparency * 100,
-      fontStyle: textStyle.IsBold && textStyle.IsItalic ? 3 : !textStyle.IsBold && !textStyle.IsItalic ? 0 : textStyle.IsBold ? 1 : 2,
-      fontSize: Math.round(textStyle.FontSize)
-    }
-
-    const fontName = await this.messageHandler.sendWithPromise('GetBaseFontName', {
-      editAreaPtr: this.editAreaPtr,
-      char: this.activeCharPlace
-    })
-    style.fontFamily = fontName
-
-    this.textStyle = style
-
-    this.eventBus.dispatch('contentPropertyChange', { type: 'image', ...style })
-  }
-
-  // 某个操作之后,获取光标所在字符的位置
-  async getCharPlace (action) {
-    const data = {
-      editAreaPtr: this.editAreaPtr,
-      char: this.activeCharPlace
-    }
-
-    if (action === 'DeleteChars') {
-      data.start = this.selectedCharRange.start
-      data.end = this.selectedCharRange.end
-    }
-
-    return await this.messageHandler.sendWithPromise(action, data)
-  }
-
-  // 根据定位点,获取选中区域的始末字符位置
-  async getCharsRange (startPoint, endPoint) {
-    return await this.messageHandler.sendWithPromise('SelectCharsRangeAtPos', {
-      pagePtr: this.pagePtr,
-      editAreaPtr: this.editAreaPtr,
-      startPoint,
-      endPoint
-    })
-  }
-
   // 保存编辑
   saveEdit () {
     this.messageHandler.sendWithPromise('EndEdit', this.editPagePtr)
@@ -1128,15 +980,6 @@ export class ImageEditor {
       if (!this.selectedCharRange && item !== 'alignType') {
         await this.selectAllText()
       }
-      
-      if (item === 'color') {
-        await this.messageHandler.sendWithPromise('SetCharsFontColor', {
-          editAreaPtr: this.editAreaPtr,
-          start: this.selectedCharRange.start,
-          end: this.selectedCharRange.end,
-          color: this.hexToRgb(props.color)
-        })
-      }
 
       if (item === 'opacity') {
         await this.messageHandler.sendWithPromise('SetCharsFontTransparency', {
@@ -1164,22 +1007,6 @@ export class ImageEditor {
           fontFamily: props.fontFamily
         })
       }
-
-      if (item === 'fontStyle') {
-        const fontStyle = props.fontStyle
-
-        await this.setCharsFontStyle('ClearCharsFontBold')
-        await this.setCharsFontStyle('ClearCharsFontItalic')
-
-        if (fontStyle === 1) {
-          await this.setCharsFontStyle('SetCharsFontBold')
-        } else if (fontStyle === 2) {
-          await this.setCharsFontStyle('SetCharsFontItalic')
-        } else if (fontStyle === 3) {
-          await this.setCharsFontStyle('SetCharsFontBold')
-          await this.setCharsFontStyle('SetCharsFontItalic')
-        }
-      }
     }
 
     const oldRect = this.rect
@@ -1189,133 +1016,94 @@ export class ImageEditor {
   // 属性面板 修改属性
   async handlePropertyPanelChanged (props) {
     if (this.state === 0) return
-
-    let changed = false
+    console.log(props)
 
     for (const item in props) {
-      if (props[item] === this.textStyle[item]) continue
-
-      if (item === 'alignType') {
-        if (!this.selectedCharRange) {
-          await this.messageHandler.sendWithPromise('SetTextAligningSection', {
-            editAreaPtr: this.editAreaPtr,
-            alignType: props.alignType,
-            char: this.activeCharPlace
-          })
-        } else {
-          await this.messageHandler.sendWithPromise('SetTextAligningRange', {
-            editAreaPtr: this.editAreaPtr,
-            alignType: props.alignType,
-            start: this.selectedCharRange.start,
-            end: this.selectedCharRange.end,
-          })
-        }
-      }
-
-      if (!this.selectedCharRange && item !== 'alignType') {
-        await this.selectAllText()
-      }
-      
-      if (item === 'color') {
-        await this.messageHandler.sendWithPromise('SetCharsFontColor', {
+      if (item === 'rotate') {
+        await this.messageHandler.sendWithPromise('RotateImage', {
           editAreaPtr: this.editAreaPtr,
-          start: this.selectedCharRange.start,
-          end: this.selectedCharRange.end,
-          color: this.hexToRgb(props.color)
+          angle: props[item]
         })
       }
 
-      if (item === 'opacity') {
-        await this.messageHandler.sendWithPromise('SetCharsFontTransparency', {
-          editAreaPtr: this.editAreaPtr,
-          start: this.selectedCharRange.start,
-          end: this.selectedCharRange.end,
-          opacity: props.opacity / 100
-        })
+      if (item === 'flip') {
+        if (props[item] === 'x') {
+          await this.messageHandler.sendWithPromise('HorizontalMirrorImage', this.editAreaPtr)
+        } else if (props[item] === 'y') {
+          await this.messageHandler.sendWithPromise('VerticalMirrorImage', this.editAreaPtr)
+        }
       }
 
-      if (item === 'fontSize') {
-        await this.messageHandler.sendWithPromise('SetCharsFontSize', {
+      if (item === 'opacity' && this.opacity !== props.opacity / 100) {
+        await this.messageHandler.sendWithPromise('SetImageTransparency', {
           editAreaPtr: this.editAreaPtr,
-          start: this.selectedCharRange.start,
-          end: this.selectedCharRange.end,
-          fontSize: props.fontSize
+          opacity: props.opacity / 100
         })
+        this.opacity = props.opacity / 100
       }
 
-      if (item === 'fontFamily') {
-        await this.messageHandler.sendWithPromise('SetFontFromNativeTrueTypeFont', {
-          editAreaPtr: this.editAreaPtr,
-          start: this.selectedCharRange.start,
-          end: this.selectedCharRange.end,
-          fontFamily: props.fontFamily
-        })
-      }
+      if (item === 'tool' && props.tool === 'replace') {
+        await this.uploadFile()
+        .then(async (data) => {
+          const { imageBase64, width, height } = data
+          const rect = {
+            top: this.frame.top,
+            left: this.frame.left,
+            width: this.frame.right - this.frame.left,
+            height: this.frame.bottom - this.frame.top
+          }
 
-      if (item === 'fontStyle') {
-        const fontStyle = props.fontStyle
+          const imageRatio = width / height
+          const rectRatio = rect.width / rect.height
+          let scaledWidth, scaledHeight
 
-        await this.setCharsFontStyle('ClearCharsFontBold')
-        await this.setCharsFontStyle('ClearCharsFontItalic')
+          if (imageRatio > rectRatio) {
+            scaledWidth = rect.width
+            scaledHeight = rect.width / imageRatio
+          } else {
+            scaledWidth = rect.height * imageRatio
+            scaledHeight = rect.height
+          }
+          
+          let left, right, bottom, top
+          const centerPoint = {
+            x: rect.left + rect.width / 2,
+            y: rect.top + rect.height / 2
+          }
 
-        if (fontStyle === 1) {
-          await this.setCharsFontStyle('SetCharsFontBold')
-        } else if (fontStyle === 2) {
-          await this.setCharsFontStyle('SetCharsFontItalic')
-        } else if (fontStyle === 3) {
-          await this.setCharsFontStyle('SetCharsFontBold')
-          await this.setCharsFontStyle('SetCharsFontItalic')
-        }
+          left = centerPoint.x - scaledWidth / 2
+          right = centerPoint.x + scaledWidth / 2
+          bottom = centerPoint.y + scaledHeight / 2
+          top = centerPoint.y - scaledHeight / 2
+
+          this.editAreaPtr = await this.messageHandler.sendWithPromise('ReplaceImageAreaByStream', {
+            pagePtr: this.pagePtr,
+            editPagePtr: this.editPagePtr,
+            editAreaPtr: this.editAreaPtr,
+            rect: {
+              left,
+              right,
+              bottom,
+              top
+            },
+            imageBase64
+          })
+
+          this.saveEdit()
+        })
+        .catch((error) => {
+          console.error(error)
+        })
       }
 
-      this.textStyle[item] = props[item]
-      changed = true
+      if (item === 'tool' && props.tool === 'export') {
+        const { imageArray } = await this.messageHandler.sendWithPromise('ExtractOriginalImage', this.editAreaPtr)
+        console.log(imageArray)
+      }
     }
 
-    if (!changed) return
-
     const oldRect = this.rect
     await this.updateCanvas(null, oldRect)
-
-    const { start, end } = await this.messageHandler.sendWithPromise('RefreshRange', {
-      editAreaPtr: this.editAreaPtr,
-      start: this.selectedCharRange.start,
-      end: this.selectedCharRange.end,
-    })
-    this.selectedCharRange = { start, end }
-
-    if (this.activeCharPlace.CharIndex === start.CharIndex) {
-      this.activeCharPlace = start
-    } else {
-      this.activeCharPlace = end
-    }
-    
-    // this.updateSelectedRect()
-  }
-
-  // 设置文本样式
-  async setCharsFontStyle (action) {
-    await this.messageHandler.sendWithPromise('SetCharsFontStyle', {
-      editAreaPtr: this.editAreaPtr,
-      start: this.selectedCharRange.start,
-      end: this.selectedCharRange.end,
-      fontStyle: action
-    })
-  }
-
-  // 十六进制颜色转换为rgb
-  hexToRgb (hex) {
-    hex = hex.replace("#", "")
-  
-    let shorthand = hex.length === 3
-  
-    hex = shorthand ? hex.repeat(2) : hex
-  
-    let r = parseInt(hex.substr(0, 2), 16)
-    let g = parseInt(hex.substr(2, 2), 16)
-    let b = parseInt(hex.substr(4, 2), 16)
-  
-    return {r, g, b}
   }
 
   // 计算编辑区域大小变化后,影响的整个区域的位置
@@ -1359,40 +1147,16 @@ export class ImageEditor {
     this.state = 1
     this.contentContainer.selectedFrameIndex = this.editAreaIndex
     this.frameContainer.classList.add('selected')
-    // this.frameContainer.classList.add('editing')
-
-    // this.activeCharPlace = {
-    //   SectionIndex: 0,
-    //   LineIndex: 0,
-    //   RunIndex: 0,
-    //   CharIndex: -1
-    // }
-
-    // const cursor = createSvg(
-    //   'svg',
-    //   {
-    //     class: 'cursor-animation'
-    //   },
-    //   {
-    //     position: 'absolute',
-    //     left: 0,
-    //     top: 0,
-    //     width: '100%',
-    //     height: '100%',
-    //   }
-    // )
-    // this.cursor = cursor
-    // this.imageContainer.append(this.cursor)
-
-    // this.updateCursorLine()
+    this.container.append(this.outerLine)
+
+    if (!isMobileDevice) {
+      this.updateOverview()
+      this.eventBus.dispatch('contentPropertyChange', { type: 'image', isOpen: true })
+    }
 
     // this.textarea.focus()
     // this.textarea.addEventListener('keydown', this.onKeydown)
     // this.textarea.addEventListener(this.textInput, this.onTextInput)
-    // if (this.isFirefox) {
-    //   this.textarea.addEventListener('compositionstart', this.onCompositionstart)
-    //   this.textarea.addEventListener('compositionend', this.onCompositionend)
-    // }
 
     onClickOutside([this.imageContainer, this.outerLine, document.querySelector('.editor-panel'), document.getElementById('propertyPanelButton')], this.handleOutside.bind(this))
   }
@@ -1412,4 +1176,78 @@ export class ImageEditor {
     
     this.contentContainer.removeEditor(this.editAreaIndex)
   }
+
+  // 上传图片并获取宽高
+  uploadFile () {
+    return new Promise((resolve, reject) => {
+      const fileInput = document.createElement('input')
+      fileInput.type = 'file'
+      fileInput.accept = '.jpg, .jpeg, .png, .bmp'
+
+      fileInput.onchange = () => {
+        const file = fileInput.files[0]
+
+        if (file.size > 2 * 1024 * 1024) {
+          reject('文件大小超过2M限制')
+          fileInput.remove()
+          window.$message.error('The file size exceeds the 2M limit.', {
+            duration: 3000,
+            // icon: () => h('img', { src: MessageError })
+          })
+          return
+        }
+
+        if (file) {
+          const reader = new FileReader()
+          reader.onload = function (e) {
+            const image = new Image()
+            image.onload = function () {
+              resolve({
+                // file,
+                imageBase64: e.target.result,
+                width: image.width,
+                height: image.height
+              })
+            }
+            image.onerror = function (error) {
+              reject(error)
+            }
+            image.src = e.target.result
+          }
+          reader.onerror = function (error) {
+            reject(error)
+          }
+          reader.readAsDataURL(file)
+          fileInput.remove()
+        }
+      }
+      fileInput.click()
+    })
+  }
+
+  // 更新属性面板预览图
+  async updateOverview() {
+    const canvasWidth = Math.ceil(this.rect.width)
+    const canvasHeight = Math.ceil(this.rect.height)
+
+    const { imageArray } = await this.messageHandler.sendWithPromise('ExtractThumbImage', {
+      editAreaPtr: this.editAreaPtr,
+      width: canvasWidth,
+      height: canvasHeight
+    })
+
+    const canvas = document.createElement('canvas')
+    const context = canvas.getContext('2d')
+
+    canvas.width = canvasWidth
+    canvas.height = canvasHeight
+    
+    const imageData = context.createImageData(canvasWidth, canvasHeight)
+    const imageDataArray = new Uint8ClampedArray(imageArray.buffer)
+    imageData.data.set(imageDataArray)
+    context.putImageData(imageData, 0, 0)
+    const overviewImageSrc = canvas.toDataURL('image/png', 1)
+
+    this.eventBus.dispatch('contentPropertyChange', { type: 'image', overviewImageSrc })
+  }
 }

+ 88 - 0
packages/core/src/worker/compdfkit_worker.js

@@ -31,6 +31,7 @@ let EditTextStyle = {}
 let U8StringData = ''
 let PDFRange = {}
 let TextRectArray = []
+let ImageAreaInfo = {}
 
 import MessageHandler from "../message_handler"
 import { convertFileToBuffer, convertBase64ToBytes } from '../fileHandler';
@@ -1232,6 +1233,68 @@ class CPDFWorker {
         imageData.length
       )
     })
+
+    messageHandler.on('ReplaceImageAreaByStream', async (data) => {
+      const { pagePtr, editPagePtr, editAreaPtr, rect, imageBase64 } = data
+
+      const imageData = convertBase64ToBytes(imageBase64)
+
+      ComPDFKitJS.opened_image = []
+      ComPDFKitJS.opened_image[0] = imageData
+
+      return Module._ReplaceImageAreaByStream(
+        pagePtr,
+        editPagePtr,
+        editAreaPtr,
+        rect.left,
+        rect.right,
+        rect.bottom,
+        rect.top,
+        0,
+        imageData.length
+      )
+    })
+
+    messageHandler.on('RotateImage', (data) => {
+      const { editAreaPtr, angle } = data
+      return Module._RotateImage(editAreaPtr, angle)
+    })
+
+    messageHandler.on('HorizontalMirrorImage', (editAreaPtr) => {
+      return Module._HorizontalMirrorImage(editAreaPtr)
+    })
+
+    messageHandler.on('VerticalMirrorImage', (editAreaPtr) => {
+      return Module._VerticalMirrorImage(editAreaPtr)
+    })
+
+    messageHandler.on('SetImageTransparency', (data) => {
+      const { editAreaPtr, opacity } = data
+      return Module._SetImageTransparency(editAreaPtr, opacity)
+    })
+
+    messageHandler.on('GetImageTransparency', (editAreaPtr) => {
+      return Module._GetImageTransparency(editAreaPtr)
+    })
+
+    messageHandler.on('ExtractOriginalImage', (editAreaPtr) => {
+      ImageAreaInfo = {}
+      Module._GetImageAreaSize(editAreaPtr)
+      console.log(res, ImageAreaInfo)
+
+      const imageArray = extractImage({
+        editAreaPtr,
+        width: ImageAreaInfo.Width,
+        height: ImageAreaInfo.Height
+      })
+      return { imageArray }
+    })
+
+    messageHandler.on('ExtractThumbImage', (data) => {
+      const { editAreaPtr, width, height } = data
+      const imageArray = extractImage(data, 1)
+      return { imageArray }
+    })
   }
 }
 
@@ -2547,3 +2610,28 @@ function createImageStamp(data) {
   const res = Module._SetImageStampByStream(annotPtr, imageData, imageData.length, 0)
   // _free()
 }
+
+function extractImage(data, isThumb) {
+  const { editAreaPtr, width, height } = data
+  let pixelNum = parseInt(width) * parseInt(height)
+  let imageBytes = pixelNum * 4
+  let imageptr = _malloc(imageBytes)
+  for (var i = 0; i < pixelNum; i++) {
+    Module.HEAP32[imageptr / 4 + i] = 0
+  }
+
+  if (isThumb) Module._ExtractThumbImage(editAreaPtr, imageptr, width, height)
+  else Module._ExtractOriginalImage(editAreaPtr, imageptr)
+
+  let imageArray = new Uint8Array(imageBytes)
+  for (var i = 0; i < imageBytes; i += 4) {
+    //bgra 转 rgba
+    imageArray[i] = Module.HEAPU8[imageptr + i + 2]
+    imageArray[i + 1] = Module.HEAPU8[imageptr + i + 1]
+    imageArray[i + 2] = Module.HEAPU8[imageptr + i]
+    imageArray[i + 3] = Module.HEAPU8[imageptr + i + 3]
+  }
+  _free(imageptr)
+
+  return imageArray
+}

+ 24 - 11
packages/webview/src/components/ContentEditorPanel/ContentEditorPanel.vue

@@ -69,14 +69,16 @@
 
     <!-- 图片编辑 -->
     <div v-else class="edit-container">
-      <div class="overview"></div>
+      <div class="overview">
+        <img :src="overviewImageSrc" alt="thumbnail">
+      </div>
       <div class="rotate">
         <div class="content-title">{{ $t('editorPanel.rotate') }}</div>
         <div class="buttons">
-          <div @click="setProperty('rotate', 'left')" :title="$t('editorPanel.rotateLeft')">
+          <div @click="setProperty('rotate', '-90')" :title="$t('editorPanel.rotateLeft')">
             <RotateImageLeft />{{ $t('editorPanel.rotateLeft') }}
           </div>
-          <div @click="setProperty('rotate', 'right')" :title="$t('editorPanel.rotateRight')">
+          <div @click="setProperty('rotate', '90')" :title="$t('editorPanel.rotateRight')">
             <RotateImageRight />{{ $t('editorPanel.rotateRight') }}
           </div>
         </div>
@@ -84,12 +86,12 @@
       <div class="flips">
         <div class="content-title">{{ $t('editorPanel.flip') }}</div>
         <div class="buttons">
-          <div @click="setProperty(0)" :title="$t('editorPanel.flipHorizontal')">
+          <div @click="setProperty('flip', 'x')" :title="$t('editorPanel.flipHorizontal')">
             <span>
               <FlipX />
             </span>
           </div>
-          <div @click="setProperty(1)" :title="$t('editorPanel.flipVertical')">
+          <div @click="setProperty('flip', 'y')" :title="$t('editorPanel.flipVertical')">
             <span>
               <FlipY />
             </span>
@@ -110,17 +112,17 @@
       <div class="tools">
         <div class="content-title">{{ $t('editorPanel.tools') }}</div>
         <div class="buttons">
-          <div @click="setProperty(1)" :title="$t('editorPanel.replace')">
+          <div @click="setProperty('tool', 'replace')" :title="$t('editorPanel.replace')">
             <span>
               <ReplaceImage />
             </span>
           </div>
-          <div @click="setProperty(2)" :title="$t('editorPanel.export')">
+          <div @click="setProperty('tool', 'export')" :title="$t('editorPanel.export')">
             <span>
               <DownloadImage />
             </span>
           </div>
-          <div @click="setProperty(3)" :title="$t('editorPanel.crop')">
+          <div @click="setProperty('tool', 'crop')" :title="$t('editorPanel.crop')">
             <span>
               <TailorImage />
             </span>
@@ -201,6 +203,8 @@
 
   const isOpen = computed(() => useViewer.isElementOpen('contentEditorPanel'))
 
+  const overviewImageSrc = ref('')
+
   watch(() => property.fontFamily, (newValue, oldValue) => {
     core.setContentEditorProperty(type.value, { fontFamily: newValue })
   })
@@ -243,6 +247,9 @@
       if (item === 'isOpen') {
         props.isOpen ? useViewer.openElement('contentEditorPanel') : useViewer.closeElement('contentEditorPanel')
       }
+      if (item === 'overviewImageSrc') {
+        overviewImageSrc.value = props[item]
+      }
 
       for (const key in property) {
         if (item === key) {
@@ -258,9 +265,7 @@
   core.addEvent('contentPropertyChange', updateProperty)
 
   const setProperty = (key, value) => {
-    const data = {}
-    data[key] = value
-    core.setContentEditorProperty(type.value, data)
+    core.setContentEditorProperty(type.value, { [key]: value })
   }
 </script>
 
@@ -479,10 +484,18 @@
       }
 
       .overview {
+        display: flex;
+        align-items: center;
+        justify-content: center;
         width: 100%;
         height: 80px;
         border: 1px solid #E2E3E6;
         background: var(--c-right-side-content-fillbox-bg);
+
+        img {
+          max-width: 90%;
+          max-height: 90%;
+        }
       }
 
       .tools,