Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support smartFilter (SoLd) #140

Open
scbizu opened this issue Jun 12, 2023 · 11 comments
Open

Support smartFilter (SoLd) #140

scbizu opened this issue Jun 12, 2023 · 11 comments
Assignees

Comments

@scbizu
Copy link

scbizu commented Jun 12, 2023

TL;DR

This issue follows #72 , but not fully related . This thread focus on the discussion about the smartFilter parsing integration (and maybe the implementation ?) .

Background

As #72 mentioned , Currently , the parser do not support parse the smartFilter of the smartObject contains . Per Adobe PSD spec , all the smartObject layer data stores in SoLd(after Photoshop CS3) or PlLd (before Photoshop CS3) , because of the lack of detail explanation of SoLd.Variable , we should read the raw data and parse it by ... umm ... guessing ?

Attempts

Since I am not familiar with node ecosystem , I use the Go library to get the raw data (especially the SoLd section) . With the psd file, I got the following result:

click to show details nullIdntTEXT%49b9a235-b7bb-534b-8a6a-747e80bf8949placedTEXT%97379f26-0cd7-5042-a0c8-6588da990816PgNmlong totalPageslongCroplong frameStepObjcnull numeratorlong denominatorlongdurationObjcnull numeratorlong denominatorlongX frameCountlongAnntlongTypelongTrnfVlLdoub{doubk`doub@ Xpdoubk`doub@ Xpdoub@Ldoub{doub@LnonAffineTransformVlLdoub{doubk`doub@ Xpdoubk`doub@ Xpdoub@Ldoub{doub@LSfilterFXStyleenabboolvalidAtPositionboolfilterMaskEnableboolfilterMaskLinkedboolfilterMaskExtendWithWhitebool filterFXListVlLsObjfilterFNm TEXTdcS_b blendOptionsObjc blendOptionsOpctUntF#Prc@YMd enumBlnMNrmlenabbool hasoptionsboolFrgCObjcRGBCRd doub@YGrn doub@T Bl doub@T BckCObjcRGBCRd doub@oGrn doub@oBl doub@oFltrObjcdcS_brigidTransform nullobj EnmrLyr OrdnTrgt rigidTypeboolpuppetShapeListVlLsObjc puppetShape rigidTypeboolVrsMlongVrsNlongoriginalVertexArraytdtaH_TY TN2&SS ËaOnfK|͇&GL�D`@At&B=B9#)BE5C0&s;C,Í_C0)*C$ Chf .C*ÕCHyCDDD3D2SDDDD}DDD~DIDDGDxDdحD#~DCD3KDʴDf^D!DfVDDLDD3CDnVD@D-#DDDDDCDCD`CDCD-CDtiCDCD4xCDTCDn1CD-FCDBDOBDKcBDDODD=/DaDKaäDbDb_DbèADbqnDb/DbDbDZcÌDcàDc`D3bæQDbÂƾDgbG;DbDYbù$D33b~D bZD9aD`D-`D`h `D`D`yDS^çpD_Ü9gD3^,]D4^RD3s^lHD33^ó D >D!]/Dj]R,#D]?DԷ]ÿ( D]Ñ%Cl]êCH]ùC']ïGC]{C]HyC ]ÉPC33]Í"CY]B]Bg]ÆA]kl3]g�]^~_ fZ>B=DVy�Di`D,DX{DAAw޶DAMyD? (`i91! gDlDDBSDDrDhD$ZeYBdCe±D!yK#D4D)+oDj*EVD~�uD=CC,Dh6dDChD-DX[D_BFDD#D:9AcD(6^6DyrDDxD杸D9DйDmDDlDDlDDDΣDDͣDӄDD)D^D0²"C˯fjC3Y C>v8DP2e,DDzDD6C!D'ÆkDCDCD%D}?@d׃CRDLCϺD@CD$% D2ӣDTM@YDU#DPDTDXDxDhD1DDDDD DDWDDD9D3DJCDE+£ >DD DD~4CDP=CD؞BDfDC?D DkXdC{SCF.y�L gCD)VDUDDtå D xD̘+BʻDqVD%DD ^>\D}D'3Ch~ D CX¨CWCw+g)D,\M?Bs/²DsDLM[CfD/BDD5D>eDCDD DܘnDD D=DNPDMWD>;C+D`|'C;D8D7CgDx!CCD_D;$BDIDlCϷD5!CoAD[9�D]"D퉋DAzDP!CaCfB#D80B`^DoBCɱAD>BCsm:DQD.DNDjJDLB2DWBOB>DYDB|DHBNA/BC:DDM7DY9CXCB@2]BD1D[DC FD(;DQC]Cıia(CFDC٤DD/DNDP?DwD.eDxBV٭CB.D[B&DgCMD1CDC[DCD[D`ֱDnB,CDCD&+C5DD DDD*CDDCD)DIDtC0DCڗDCӛD%CDs_xCjØD"D>DDsD85D6ʴD[HDXD'CHDNkD?8²Db!BD7#C@SD;D_jDD)WDDJDDg{DD(aDt4DIDnDYDD4YgDWDIDL"DЂDwrD$DH DKaC5-D'bA/N3D)BEDZC]ZCBfDB5JDBjfC'C8CCC)wCpCoD^D&DD,ͪD@Dm'DC:vDKDpDCCnD5GC?5DÀC|CSVDDyCD?"DxѼDDYDDDࠗBJDC$CD!CD4BtwDqm CDɋDC?DᜁC%xD OD~X{D#CDDI|C]"D Cb DJCn8CzC8DSD{D DæDFDDVd)D Db>D2gDKWD6DFDMzUD!C$DnC0?C?ND}ڝCoDZCJZ!DfAC| DCJD yCl DdD#DiDشÂlHDyÒQD)bZD+ dD!\mDv|vD#δ}~DOD٦�D�D|�pD4Cl4DT©qD@�9D(G/ţD-èD DCDgD HD' DD;DDDI$D#DDԈ,DBDb5D`D@>D'DGD>DPD؀D FD;IDDtDVmD?qD,͟&DaHpD(="Dk?QDyBDJBXD BD($C=DVRCdDC.DCZDeC?DCDgCQDqC YD5D{BbDO%D&kDDtsD2DV |Db`DDKCDD|+D2DDS%DD&vDDӘDeD=^DkD׾DV?Dpã6B!: B5,A`/*Vz#c%ü= ZÉsI'r ojãiaFyî0B:BDdݩ :Z߉DR`D@qL DWD̶A}DvBDplDDDDD& 0bD$COD35wDOwDV&eD(D hBDdf($CÏdÍC=#D &gDD??fD=*ʄDA DxDFBDϲ BDwDDD D @dldxǬDTDP4DkDrDXDaDDD#'DDkDPvD\DڔDsOB$k}hCWB CGC4FD%D WDjCDQC~D.ɖD@ADQ6C pDmC4ԿD/DDljDmDD4B D~DSD|Dt5D^DoD9DY߼D:#KD5DD>D7DZDDԃDVNJDD;SDnx�6DDD9D CD٘CUDgBiD7sD/ChD?'DA^BXZDXM "DBPAR!~DDBCLDED¾D̚%CfC"AsD|Dc3BD4bD0§D$"λD/DZD̓aC坴bAD8«ԸCoOClrDwiNL*L,ûRDÑC.�CDl`BIRDGD2rDD]׸C4DDD5gDZDFDoxD퓦DTDu_D&O0C]DRGCʨD' D{CXDlDC*BYDDٞPAD@D)DC �D?A$LDCBЙ@Dbfc)4DD@D:~DlVDp$DuA(ADmDD,C�BDCvDk @DpC9KC8@hBa"Z/D7DDLC:ED=DkC'C#A+bBsJDǦC ȢD٩DG3~D{hDDDWD5mpD~BC * �:D6gBDpCD"CCȞD1yCd DwDDDDE CxCD.CDCDfC#DDZƹD8tC[2DLC-DoC2Di$DkDdC;DVCuDJsCiĩD1C DbhCDDrD D5D1D~CC~C/$C�CRCCоDqrDxD'DQD|D}DkC.tDWLDIkqDpCsDCuDͱCnCbD"AfCADDD6eD}DFDD)>BYCtC0VCDBgD3CD$BtuCD CDK]DTDDTCIJDtCU cDD\D]tKD`xDH:DqfDQ/D$؂D/RD1ӍDBDGD00C0D$DnNDܱyD1IDYCt4D72DwjD@MDtRDiD^mdDԐDHlDlfCzD9Dr/Dp:DM.D2D\~D4D޳D+D ;DC4:/Dw DwCU(D|CMSDC6C.2C?mD]DugDtjDwDsdDd }DD indexArraytdta$`_`mn
                '''&""*)''#$-/\-$*)

DE$),$P|}]Iw2^
u
:D9wo$8
@:;)2 ^<;stSTIz*$ VE( R2
[\0x(TUR2 JI0yz\c4%)=%wA#)*]HO]Q"
A(>$p0/A

$%

XW
sr
,5/!c c34I 5CDKtu<=ON#" 8!7PQC!T!?[!"%'%&*+)('(./-.,+?+Kbc-+-`4:

&'03KL45c7!6=>
.LQ?>,-=K120/9:(V"Q+(!9302x(HI !GH+
.A6FEA@$E)465@e
AB H"A@? &3=)#[H,X}|
LM6!{z
[ZG{|
kl&"hjkjhi&'mlpq{0
GF(]vrrq&opno YcVMNN,e.dQBC5RQ __^dcm
(UP7u-kg
_YZfeXYK''gOPfg )%"&u/
6d.8z{0 1-.
,5<K H7U=::4/{}9vyJpI<0xwv6SN3[?hl@7\VEPCEU,>X/uuZ4>K3W?_?^yDpfpJmqsht8uYMqB<qJqqmrsrmK>>>XTWO@77u/8Y||RG22KWaBMT5XZTUI@;8ZMT7< J|Y pinOffsetsVlLs doub(B3 doub@5E doub@Kp0m doub@"рe doub@G}@0doubdoubMD[doub@^vpRdoub=E*2doub@fdoub#rgdoubHXV posFinalPinsVlLs doub@}doub@rdoub@|8doub@doub@4doub@{(doub@tdoub@Ldoub@<doub@doub@doub@TpinVertexIndicesVlLslonglonglonglonglonglongPinPVlLs doub@} doub@rdoub@CJdoub@4doub@4 Adoub@{(>doub@t[doub@L !doub@;CBdoub@>Xkdoub@t 9Ndoub.PnRtVlLslonglong[long_longelonglongTPnOvVlLsboolboolboolboolboolboolPnDpVlLsdoub?pdoub?xdoub?doub?doub?doub? meshQualimeshExpansionlong meshRigiditylongimageResolutiondoub@TmeshBoundaryPathObjc pathClasspathComponentsVlLsObjcPaCmshapeOperationenumshapeOperationxorSbpLVlLsObjcSbplClspboolPts VlLsBObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxlj@Fwd ObjcPnt HrznUntF#Pxli VrtcUntF#PxljEx.Bwd ObjcPnt HrznUntF#PxlkѷYVrtcUntF#Pxlu$SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxlf VrtcUntF#PxliFwd ObjcPnt HrznUntF#PxleuY|VrtcUntF#PxliBwd ObjcPnt HrznUntF#PxlgɉVrtcUntF#Pxlj<SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxld VrtcUntF#PxliFwd ObjcPnt HrznUntF#Pxlb҂dVrtcUntF#Pxli)EhBwd ObjcPnt HrznUntF#PxldʦL/{VrtcUntF#PxliSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl_VrtcUntF#PxliFwd ObjcPnt HrznUntF#Pxl[?KVrtcUntF#PxlhgBwd ObjcPnt HrznUntF#Pxla%:P VrtcUntF#Pxlil"SmooboolObjcPthpAnchObjcPnt HrznUntF#PxlT@VrtcUntF#PxlhFwd ObjcPnt HrznUntF#PxlQUhr VrtcUntF#Pxlhʫ6zBwd ObjcPnt HrznUntF#PxlW WpVrtcUntF#PxliDiTXBSmooboolObjcPthpAnchObjcPnt HrznUntF#PxlGVrtcUntF#PxlhFwd ObjcPnt HrznUntF#Pxl,FYVrtcUntF#PxlgkkBwd ObjcPnt HrznUntF#PxlL/wVrtcUntF#PxlhTɅoSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@JVrtcUntF#Pxlg@Fwd ObjcPnt HrznUntF#Pxl@QVrtcUntF#Pxlg 1&Bwd ObjcPnt HrznUntF#Pxl@4pVrtcUntF#Pxlh#YSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@[@VrtcUntF#PxlfFwd ObjcPnt HrznUntF#Pxl@c,4pbVrtcUntF#PxleJAkBwd ObjcPnt HrznUntF#Pxl@V!-w2VrtcUntF#PxlfSSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@pVrtcUntF#Pxld@Fwd ObjcPnt HrznUntF#Pxl@pZXy=VrtcUntF#Pxld5U=Bwd ObjcPnt HrznUntF#Pxl@j'YVrtcUntF#PxleȹSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@qVrtcUntF#Pxld Fwd ObjcPnt HrznUntF#Pxl@r(|VrtcUntF#PxlcBwd ObjcPnt HrznUntF#Pxl@pW&VrtcUntF#Pxld*d8SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@+VrtcUntF#Pxlc Fwd ObjcPnt HrznUntF#Pxl@tXy=VrtcUntF#PxlcU=Bwd ObjcPnt HrznUntF#Pxl@sw VrtcUntF#PxlcĜ?2SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@uVrtcUntF#PxlcFwd ObjcPnt HrznUntF#Pxl@w5:?VrtcUntF#PxlbE\"aBwd ObjcPnt HrznUntF#Pxl@uEW&VrtcUntF#Pxlc Bwd ObjcPnt HrznUntF#PxllU=VrtcUntF#Pxl@SmooboolObjcPthpAnchObjcPnt HrznUntF#PxllVrtcUntF#Pxl@Fwd ObjcPnt HrznUntF#PxllVrtcUntF#Pxl@X*0Bwd ObjcPnt HrznUntF#PxlkkOVrtcUntF#[email protected] HrznUntF#PxllVrtcUntF#Pxl@Fwd ObjcPnt HrznUntF#PxllYpVrtcUntF#Pxl@j(Bwd ObjcPnt HrznUntF#PxllVrtcUntF#Pxl@fBSmooboolObjcPthpAnchObjcPnt HrzBwd ObjcPnt HrznUntF#Pxlko@VrtcUntF#Pxl@%Ҡ2~SmooboolObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@Fwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@J2XBwd ObjcPnt HrznUntF#Pxlku\1VrtcUntF#Pxl@~VQSmooboolObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@ Fwd ObjcPnt HrznUntF#PxlkvVVrtcUntF#Pxl@$]VrtcUntF#Pxl[n.Bwd ObjcPnt HrznUntF#Pxl@lOVrtcUntF#Pxl]$%SmooboBwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@5N;5SmooboolObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@Fwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@uBwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@}d VrtcUntF#PxlZ\NSmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@Vl#SmooboolObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@vFwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@oK]Bwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@|??SmooboolObjcPthpAnchObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@9Fwd ObjcPnt HrznUntF#Pxlk.HVrtcUntF#PxlJlBwd ObjcPnt HrznUntF#PxlkVrtcUntF#Pxl@aT74SmooboolSFwd ObjcPnt HrznUntF#Pxl@?n_&[VrtcUntF#PxlS:)$wd ObjcPnt HrznUntF#Pxl@gVrtcUntF#PxlU?Ik^SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@HVrtcUntF#PxlS@Fwd ObjcPnt HrznUntF#Pxl@RpselectedPinVlLslongPuX0doub{PuX1doub@#Pxl@04VXpPuX2doub@SXpPuX3doub{PuY0doubkPuY1doubk`PuY2doub@LPuY3doub@filterIDlongcomploncompInfoObjcnullcompIDlongoriginalCompIDlongImprObjcNonenonewarpObjcwarp warpStyleenum warpStyle SmooboolObjcPthpAnchObjwarpCustomntF#PxwarpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoubPBwd ObjcPnt HrznUntF#Pxl@kVrtcUntF#PxlR0SmooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@VrtcUntF#PxlQFwd OwarpRotateenumOrntHrznboundsObjcclassFloatRectTop doubLeftdoubBtomdoub@@Rghtdoub@@uOrderlongvOrderlongcustomEnvelopeWarpObjccustomEnvelopeWarp HrznUntF#Pxl@icVrtcUntF#PxlP8rationalPointHrznUnFl#Pxl@ĔN@2d3z@oR[@(<@pt$mooboolObjcPthpAnchObjcPnt HrznUntF#Pxl@VrtcUntF#PxlPFwd ObjcPnt HrznUntF#Pxl@L\VrtcUntF#PxlNc
@pyd@D#@zu@A3M@UUUUU@UUUUU@@VrtcUnFl#Pxl@ .4Bwd ObjcPnt HrznUntF#Pxl@SVrtcUntF#PxlQVN w]7@kzW@[!oB@UUUUW@G�@@FF3u{@uuuuu@w=Iy@
}@XX#@@@@@@@@oboolObjcPthpAnchObjcPnt HrznUntF#Pxl@8VrtcUntF#PxlPFwd ObjcPnt HrznUntF#Pxl@>VrtcUntF#PxlOBwd ObjcPnt HrznUntF#Pxl@&&#pVrtcUntF#PxlPUSmooboolObjcPthpAnchObjcP

I compare the raw data with the SoLdDescriptor interface , I found that the filterFXList is missing , It seems that it contains the field: puppetShapeList, which contains a series of data streams (seems like some binary data) , I am stuck in reading this out , and ask here if you have some exps in reading this ?

Some Code Snippet

// The parse.go to parse the psd layer SoLd data 
package main

import (
	"fmt"
	"os"

	"github.com/oov/psd"
)

func main() {
	f, err := os.Open("resource/smart_filter.psd")
	if err != nil {
		panic(err)
	}
	// psd.Debug = log.New(os.Stdout, "psd: ", log.Lshortfile)
	p, _, err := psd.Decode(f, &psd.DecodeOptions{
		SkipLayerImage:  false,
		SkipMergedImage: false,
	})
	if err != nil {
		panic(err)
	}
	for _, l := range p.Layer {
		if l.Name == "组 5" {
			for _, ll := range l.Layer {
				if ll.Name == "qianpian" {
					for key, data := range ll.AdditionalLayerInfo {
						// fmt.Printf("key: %s\n", key)
						if key == "SoLd" {
							readRawData(data)
						}
					}
				}
			}
		}
	}
}

func readRawData(data []byte) {
	fmt.Printf("section len: %d\n", len(data))
	fmt.Printf("first 4 bytes[Identifier]: %v\n", string(data[:4]))
	data = data[4:]
	fmt.Printf("section len: %d\n", len(data))
	fmt.Printf("later 4 bytes[Version]: %#v\n", string(data[:4]))
	data = data[4:]
	fmt.Printf("section len: %d\n", len(data))
	fmt.Printf("later 4 bytes[Descriptor Version]: %#v\n", string(data[:4]))
	data = data[4:]
	fmt.Printf("section len: %d\n", len(data))
	fmt.Printf("later 4 bytes[Variable]: %v\n", string(data))
}
@scbizu scbizu changed the title Support for smartFilter (SoLd) Support smartFilter (SoLd) Jun 12, 2023
@Agamnentzar
Copy link
Owner

SoLd is actually smart object layer section, it doesn't contain smart filter data. Smart filter data in this file is in the FEid section.

@scbizu
Copy link
Author

scbizu commented Jun 13, 2023

Ok , I will take a look at it

@scbizu scbizu changed the title Support smartFilter (SoLd) Support smartFilter (FEid) Jun 13, 2023
@scbizu
Copy link
Author

scbizu commented Jun 15, 2023

@Agamnentzar Sorry to bother again , I read the spec and do some parsing stuff manually to the FEid but I still cannot get detail filter params .

Here is what I got:

  • Version: 3
  • Length: 540318
  • Identifier: 97379f26-0cd7-5042-a0c8-6588da990816
  • Version: 1
  • Length: 5394183
  • Rect: (-444,-222)-(1604,2006)
  • Depth: 8
  • MaxChannel: 24
    (Channel 0)
  • Boolean: 1
  • Length: 1908433
  • CompressionMode: 3
  • Data: (Channel 0 ImageData) <- (1908433 -2 ) bytes
    (Channel 1)
  • Boolean: 1
  • Length: 1784669
  • CompressionMode: 3
  • Data: (Channel 1 ImageData) <- (1784669 -2 ) bytes
    (Channel 2)
  • Boolean: 1
  • Length: 1689873
  • CompressionMode: 3
  • Data: (Channel 2 ImageData) <- (1689873 -2) bytes
    (Skip 88 bytes, IDK why)
    (UserMask)
  • Boolean: 1
  • Length: 11048
  • CompressionMode: 3
  • Data: (Mask ImageData)
    (Rest 8960 bytes cannot read)

I got the RGB image , but the image is just the image in smartObject layer , do not have any effect . If I change the smartFilter control points , there is nothing happened to the extract image from FEid , but if I re-put this psd into PS , it can read the changed control points , the real info is not stored in the FEid ? Any thoughts ?

(If you are interested in the parsing process code , I can share it with u , it is written in Go , so I not paste here)

@Agamnentzar
Copy link
Owner

I wrote parsing code like this, but for some reason it is ending up on the wrong byte after first filter entry, and the version is incorrect

		const version = readInt32(reader);
		if (version < 1 || version > 3) throw new Error(`Invalid filterEffects version ${version}`);

		if (readUint32(reader)) throw new Error('filterEffects: 64 bit length is not supported');
		const length = readUint32(reader);
		const end = reader.offset + length;

		while (reader.offset < end) {
			console.log('bytes to go', end - reader.offset, 'at', reader.offset.toString(16));
			//
			const id = readPascalString(reader, 1);
			const effectVersion = readInt32(reader);
			if (effectVersion !== 1) throw new Error(`Invalid filterEffect version ${effectVersion}`);
			if (readUint32(reader)) throw new Error('filterEffect: 64 bit length is not supported');
			const effectLength = readUint32(reader);
			const endOfEffect = reader.offset + effectLength;
			const top = readInt32(reader);
			const left = readInt32(reader);
			const bottom = readInt32(reader);
			const right = readInt32(reader);
			const depth = readInt32(reader);
			const maxChannels = readInt32(reader);
			const channels: any[] = [];

			for (let i = 0; i < (maxChannels + 2); i++) {
				const exists = readInt32(reader);
				if (exists) {
					if (readUint32(reader)) throw new Error('filterEffect: 64 bit length is not supported');
					const channelLength = readUint32(reader);
					const compressionMode = readUint16(reader);
					const data = readBytes(reader, channelLength - 2);
					channels.push({ channelLength, compressionMode, data: data?.length + ' bytes' });
				} else {
					channels.push(undefined);
				}
			}

			console.log('left at the end', endOfEffect - reader.offset);
			if (endOfEffect > reader.offset) {
				if (readUint8(reader)) {
					const compressionMode = readUint16(reader);
					const data = endOfEffect > reader.offset ? readBytes(reader, endOfEffect - reader.offset) : undefined;
					console.log('extra data', { compressionMode, data: data?.length + ' bytes' });
				} else {
					console.log('no extra');
				}
			}

			console.log('effect', {
				id,
				effectVersion,
				effectLength,
				top,
				left,
				bottom,
				right,
				depth,
				maxChannels,
				channels,
			});

			console.log('bytes left after effect', endOfEffect - reader.offset);
			// if (length % 4) skipBytes(reader, 4 - length % 4);
		}

		console.log({ version, length });

I don't really know where the control points might be stored, they should be somewhere here, but spec only mentions these RGB bitmap entries, so not sure where it would be. Could try diff on both files and check all sections that changed.

@scbizu
Copy link
Author

scbizu commented Jun 16, 2023

@Agamnentzar Thank you , maybe I can help parsing the FEid here , but after read the parsing result , I think this section maybe useless (at least for extracting PuppetShape's Control Point) . As for I saw PuppetShape before in SoLd , I'd like to check SoLd again , and to see if I can make some progress.

@Agamnentzar
Copy link
Owner

@scbizu ok I found it, it is in SoLd section, I'll look into implementing it properly. It should be fine for reading, although I'm not sure if for writing the FEid section wouldn't have to be written correctly too, for the filter to work properly.

@scbizu
Copy link
Author

scbizu commented Jun 16, 2023

Sounds awesome 👍 , today I do not have enough time to look at this , tomorrow I will try , thanks for inspiring me :)

@Agamnentzar
Copy link
Owner

Released version 15.3.0 with support for reading and writing smart layer filter. Might still have some issues but seems to work on test document.

@scbizu scbizu changed the title Support smartFilter (FEid) Support smartFilter (SoLd) Jun 17, 2023
@scbizu
Copy link
Author

scbizu commented Jun 17, 2023

@Agamnentzar I see the PuppetShapeList now , so many args and hard to know actual though XD , seems hard to re-apply to other image use Canvas API

@Agamnentzar
Copy link
Owner

I think it mainly uses mesh of triangles that is being distorted, so it could probably be implemented with webgl, although the quality might not be the best then. I don't know what are most of the other parameters for, but maybe just for controlling the deformation UI.

@scbizu
Copy link
Author

scbizu commented Jun 20, 2023

I found that those params maybe refers to the ARAP(A.K.A As-Rigid-As-Possible Shape Manipulation) , and the source paper is here .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants