genai-for-marketing/frontend/src/app/social-media-edit-canvas/social-media-edit-canvas.component.ts (209 lines of code) (raw):

/** * Copyright 2024 Google LLC * * 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. */ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'; import { CampaignNames } from '../email-copy/email-copy.component'; import { FormGroup, FormControl } from '@angular/forms'; import { EmailCopyService } from '../services/email-copy.service'; import { DomSanitizer } from '@angular/platform-browser'; import { BOLD_BUTTON, EditorConfig, FONT_SIZE_SELECT, FORE_COLOR, IMAGE_INPUT, INDENT_BUTTON, ITALIC_BUTTON, JUSTIFY_CENTER_BUTTON, JUSTIFY_FULL_BUTTON, JUSTIFY_LEFT_BUTTON, JUSTIFY_RIGHT_BUTTON, LINK_INPUT, ORDERED_LIST_BUTTON, OUTDENT_BUTTON, SEPARATOR, STRIKE_THROUGH_BUTTON, ST_BUTTONS, SUBSCRIPT_BUTTON, SUPERSCRIPT_BUTTON, UNDERLINE_BUTTON, UNDO_BUTTON, UNLINK_BUTTON, UNORDERED_LIST_BUTTON } from 'ngx-simple-text-editor'; @Component({ selector: 'app-social-media-edit-canvas', templateUrl: './social-media-edit-canvas.component.html', styleUrl: './social-media-edit-canvas.component.scss' }) export class SocialMediaEditCanvasComponent implements AfterViewInit, OnChanges { editImageSection: boolean = false; base64String: any; @Output() showSaveButton: EventEmitter<boolean> = new EventEmitter<boolean>(); edit_mask_tools: CampaignNames[] = [{ name: "Rectangle" }, { name: "Brush" }, { name: "Circle" }, { name: "Move/Scale/Rotate" }]; @ViewChild('myCanvas', { static: false }) canvas!: ElementRef<HTMLCanvasElement> | any; @ViewChild('scream', { static: false }) imgx!: ElementRef<HTMLImageElement> | any; private ctx!: CanvasRenderingContext2D | any; private img: HTMLImageElement = new Image(); private isDrawing: boolean = false; private startX!: number; private startY!: number; uploadedEditImageForm = new FormGroup({ selectedTool: new FormControl(), promptMsg: new FormControl(), }); config: EditorConfig = { placeholder: 'Type something...', buttons: [UNDO_BUTTON, SEPARATOR, BOLD_BUTTON, ITALIC_BUTTON, UNDERLINE_BUTTON, STRIKE_THROUGH_BUTTON, JUSTIFY_LEFT_BUTTON, JUSTIFY_CENTER_BUTTON, JUSTIFY_RIGHT_BUTTON, JUSTIFY_FULL_BUTTON, ORDERED_LIST_BUTTON, UNORDERED_LIST_BUTTON, INDENT_BUTTON, OUTDENT_BUTTON, SUBSCRIPT_BUTTON, SUPERSCRIPT_BUTTON, FONT_SIZE_SELECT, LINK_INPUT, UNLINK_BUTTON, FORE_COLOR, IMAGE_INPUT] }; @Input() imageSrc: any imageData: any; promptGeneratedImages!: any[]; emailCopy!: any; showProgress: boolean = false; showImagesGenerated: boolean = false; image_base64: string | undefined; //Declare the property @Output() imageBase64Change: EventEmitter<any> = new EventEmitter<any>(); //textContent @Output() emailTextContent: EventEmitter<any> = new EventEmitter<any>(); @Input() selectedCampaignFromDropdown: any showEmailEditor: boolean = false; showGenerateImagesSpinner: boolean = false; textContent: string = ""; showGenerateEmailBtn: boolean = false; selectedImage: any; showEmailCopySave: boolean = false; selectButtonId: any; maskedBase64String: any; selectDisable: boolean = false; //Raise the event to send the data back to parent constructor(public emailService: EmailCopyService, private domSanitizer: DomSanitizer, ) { } ngOnChanges() { /**********THIS FUNCTION WILL TRIGGER WHEN PARENT COMPONENT UPDATES 'someInput'**************/ this.ngAfterViewInit(); } editMaskTools(selectedTool: any) { console.log(selectedTool) } onMouseDown(event: MouseEvent): void { event.preventDefault(); event.stopPropagation(); this.buildMyCanvas(); this.isDrawing = true; this.startX = event.clientX - this.canvas.nativeElement.getBoundingClientRect().left this.startY = event.clientY - this.canvas.nativeElement.getBoundingClientRect().top; var scaleX = this.canvas.nativeElement.width / this.canvas.nativeElement.getBoundingClientRect().width; var scaleY = this.canvas.nativeElement.height / this.canvas.nativeElement.getBoundingClientRect().height; this.startX *= scaleX; this.startY *= scaleY; } onMouseUp(event: MouseEvent): void { if (this.isDrawing) { event.preventDefault(); event.stopPropagation(); this.buildMyCanvas(); var currentX = event.clientX - this.canvas.nativeElement.getBoundingClientRect().left; var currentY = event.clientY - this.canvas.nativeElement.getBoundingClientRect().top; var scaleX = this.canvas.nativeElement.width / this.canvas.nativeElement.getBoundingClientRect().width; var scaleY = this.canvas.nativeElement.height / this.canvas.nativeElement.getBoundingClientRect().height; currentX *= scaleX; currentY *= scaleY; const width = currentX - this.startX; const height = currentY - this.startY; if (width > 0 && height > 0) { this.ctx.strokeRect(this.startX, this.startY, width, height); this.isDrawing = false; this.imageData = this.ctx.getImageData(this.startX, this.startY, width, height); this.createMask() } } } createMask() { let srcImg = this.ctx.getImageData(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height) let destImg = this.ctx.createImageData(srcImg) let destData = destImg.data for (let i = 0; i < destData.length; i++) { destData[i] = 0; } let copyImageData = this.imageData; for (let i = 0; i < copyImageData.data.length; i++) { copyImageData.data[i] = 255; } let clone = this.canvas.nativeElement.cloneNode(); let cloneCtx = clone.getContext('2d') cloneCtx.putImageData(destImg, 0, 0); cloneCtx.putImageData(copyImageData, this.startX, this.startY); var url = clone.toDataURL('image/jpeg'); this.maskedBase64String = url.substring('data:image/jpeg;base64,'.length); console.log(this.maskedBase64String); } onMouseMove(event: MouseEvent): void { event.preventDefault(); event.stopPropagation(); if (!this.isDrawing) return; var currentX = event.clientX - this.canvas.nativeElement.getBoundingClientRect().left; var currentY = event.clientY - this.canvas.nativeElement.getBoundingClientRect().top; var scaleX = this.canvas.nativeElement.width / this.canvas.nativeElement.getBoundingClientRect().width; var scaleY = this.canvas.nativeElement.height / this.canvas.nativeElement.getBoundingClientRect().height; currentX *= scaleX; currentY *= scaleY; const width = currentX - this.startX; const height = currentY - this.startY; this.buildMyCanvas(); this.ctx.strokeRect(this.startX, this.startY, width, height); } onMouseOut(event: MouseEvent): void { event.preventDefault(); event.stopPropagation(); this.buildMyCanvas(); } private getBase64StringFromDataURL = (dataURL: any) => dataURL.replace('data:', '').replace(/^.+,/, ''); ngAfterViewInit() { const canvas: HTMLCanvasElement = this.canvas?.nativeElement; this.ctx = (canvas?.getContext('2d')); this.img.src = this.imageSrc this.img.onload = () => { this.ctx.clearRect(0, 0, this.img.width, this.img.height); this.drawImageScaled(this.img, this.ctx) }; fetch(this.imageSrc) .then((res) => res.blob()) .then((blob) => { // Read the Blob as DataURL using the FileReader API const reader = new FileReader(); reader.onloadend = () => { // Convert to Base64 string this.base64String = this.getBase64StringFromDataURL(reader.result); }; reader.readAsDataURL(blob); }); } buildMyCanvas() { this.ctx.clearRect(0, 0, this.img.width, this.img.height); this.drawImageScaled(this.img, this.ctx) this.ctx.lineWidth = 1; // Border width this.ctx.strokeStyle = 'blue'; // Border color } drawImageScaled(img: HTMLImageElement, ctx: CanvasRenderingContext2D) { var canvas = ctx.canvas; window.devicePixelRatio = 2; var size = 378; canvas.style.width = size + "px"; canvas.style.height = size + "px"; var scale = window.devicePixelRatio; canvas.width = Math.floor(size * scale); canvas.height = Math.floor(size * scale); var hRatio = canvas.width / img.width; var vRatio = canvas.height / img.height; ctx.imageSmoothingEnabled = false; ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width * hRatio, img.height * vRatio); } generate() { this.showGenerateImagesSpinner = true; let obj = { "prompt": this.uploadedEditImageForm.controls.promptMsg.value, "base_image_base64": this.base64String, "mask_base64": this.maskedBase64String } this.emailService.editImage(obj).subscribe((res: any) => { this.promptGeneratedImages = [] res.generated_images.forEach((element: { images_base64_string: string; id: any }) => { this.emailCopy = this.domSanitizer.bypassSecurityTrustResourceUrl('data:image/jpg;base64,' + element.images_base64_string); this.promptGeneratedImages.push({ id: element.id, image: this.emailCopy }); this.showGenerateImagesSpinner = false; this.showProgress = false; this.showImagesGenerated = true }); }) } onSelectImage(image: any, id: any) { this.showGenerateEmailBtn = true; this.showEmailEditor = true; this.selectButtonId = id; this.selectDisable = true; //emit imageBase64String to the parent component this.updatedImage(image); } updatedImage(image : any) { this.imageBase64Change.emit(image); // if(this.textContent){ // this.emailTextContent.emit(this.textContent); // } } onClickSelect(image: any) { this.selectedImage = image.changingThisBreaksApplicationSecurity; this.showSaveButton.emit(true); //this.selectButtonClick = true; } }