def bbox(self): if len(self.stored_list) == 0: return BBox(0, 0, 0, 0) min_x1 = min([x.bbox[0] for x in self.stored_list]) min_y1 = min([x.bbox[1] for x in self.stored_list]) max_x2 = max([x.bbox[2] for x in self.stored_list]) max_y2 = max([x.bbox[3] for x in self.stored_list]) return BBox(min_x1, min_y1, max_x2, max_y2)
def as_stored_image(self): """ 把自身转化为合并后的单个的StoredImage 12.31:从collapse...函数改造 :return: 合并完成的StoredImage """ if not isinstance(self, StoredGroup): raise TypeError("Not a StoredGroup type being collapsing") group_image = new_blank_pic_as_PIL(self.bbox.width, self.bbox.height) for stored_image in self.stored_list[::-1]: relative_bbox = BBox(stored_image.bbox.x1 - self.bbox.x1, stored_image.bbox.y1 - self.bbox.y1, stored_image.bbox.x2 - self.bbox.x1, stored_image.bbox.y2 - self.bbox.y1) # 计算图片在整个组中的相对边界。组的边界是由整个组内部所有成员的边界确定的 if isinstance(stored_image, StoredGroup): op_image = stored_image.as_stored_image().final_image_as_PIL else: op_image = stored_image.final_image_as_PIL temp_image = new_blank_pic_as_PIL(self.bbox.width, self.bbox.height) # 创建一张和整个组一样大的空图 temp_image.paste(op_image, (relative_bbox.x1, relative_bbox.y1)) # 虽然带透明度的两张图无法直接paste,但是贴到一张全透明的空图的中却可以 group_image = Image.alpha_composite(group_image, temp_image) # 带透明度没法直接paste,必须alpha_composite return StoredImage(None, group_image, copy.copy(self.info), copy.copy(self.bbox))
def test_userapi_placed_layers(): img = PSDImage(decode_psd("placedLayer.psd")) bg = img.layers[3] assert bg.placed_layer_size is None assert bg.transform_bbox is None layer0 = img.layers[0] assert layer0.placed_layer_size == (64, 64) assert layer0.transform_bbox == BBox(x1=96.0, y1=96.0, x2=160.0, y2=160.0) layer1 = img.layers[1] assert layer1.placed_layer_size == (101, 55) assert layer1.placed_layer_size.width == 101 assert layer1.transform_bbox == BBox(x1=27.0, y1=73.0, x2=229.0, y2=183.0) layer2 = img.layers[2] assert layer2.placed_layer_size == (64, 64) assert layer2.transform_bbox == BBox(x1=96.0, y1=96.0, x2=160.0, y2=160.0)
def collapse_group_into_stored_image(stored_group): """ 把一个自己的StoredGroup合并成一个StoredImage,就像Photoshop里的合并图层 还差的地方:如果bbox中信息和实际图像大小不符的话? 另外未考虑NORMAL以外的混合模式 未考虑各种蒙版,因为PSD实在有点复杂 12.29:边缘总是会多一个像素,考虑crop 12.30:边缘多一个像素被证明是图层内容本来就超出边界的原因 :param stored_group: :return:一个合并完成的StoredImage """ if not isinstance(stored_group, StoredGroup): raise TypeError("Not a StoredGroup type being collapsing") group_image = new_blank_pic_as_PIL(stored_group.bbox.width, stored_group.bbox.height) for stored_image in stored_group.stored_list[::-1]: relative_bbox = BBox(stored_image.bbox.x1 - stored_group.bbox.x1, stored_image.bbox.y1 - stored_group.bbox.y1, stored_image.bbox.x2 - stored_group.bbox.x1, stored_image.bbox.y2 - stored_group.bbox.y1) # 计算图片在整个组中的相对边界。组的边界是由整个组内部所有成员的边界确定的 if isinstance(stored_image, StoredGroup): op_image = collapse_group_into_stored_image( stored_image).final_image_as_PIL else: op_image = stored_image.final_image_as_PIL temp_image = new_blank_pic_as_PIL(stored_group.bbox.width, stored_group.bbox.height) # 创建一张和整个组一样大的空图 temp_image.paste(op_image, (relative_bbox.x1, relative_bbox.y1)) # 虽然带透明度的两张图无法直接paste,但是贴到一张全透明的空图的中却可以 group_image = Image.alpha_composite(group_image, temp_image) # 带透明度没法直接paste,必须alpha_composite return StoredImage(None, group_image, copy.copy(stored_group.info), copy.copy(stored_group.bbox))
('gradient fill.psd', (100, 150)), ('group.psd', (100, 200)), ('hidden-groups.psd', (100, 200)), ('hidden-layer.psd', (100, 150)), ('history.psd', (100, 150)), ('mask.psd', (100, 150)), ('note.psd', (300, 300)), ('pen-text.psd', (300, 300)), ('smart-object-slice.psd', (100, 100)), ('transparentbg.psd', (100, 150)), ('transparentbg-gimp.psd', (40, 40)), ('vector mask.psd', (100, 150)), ) BBOXES = ( ('1layer.psd', 0, BBox(0, 0, 101, 55)), ('2layers.psd', 0, BBox(8, 4, 93, 50)), ('2layers.psd', 1, BBox(0, 0, 101, 55)), ('group.psd', 0, BBox(25, 24, 66, 98)) ) RESOLUTIONS = ( ('1layer.psd', ResolutionInfo( h_res=72.0, h_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, v_res=72.0, v_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, width_unit=DimensionUnit.INCH, height_unit=DimensionUnit.INCH)), ('group.psd', ResolutionInfo( h_res=72.0, h_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, v_res=72.0, v_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, width_unit=DimensionUnit.CM, height_unit=DimensionUnit.CM)), )
('hidden-layer.psd', (100, 150)), ('history.psd', (100, 150)), ('mask.psd', (100, 150)), ('note.psd', (300, 300)), ('pen-text.psd', (300, 300)), ('smart-object-slice.psd', (100, 100)), ('transparentbg.psd', (100, 150)), ('transparentbg-gimp.psd', (40, 40)), ('vector mask.psd', (100, 150)), ('gray0.psd', (400, 359)), ('gray1.psd', (1800, 1200)), ('empty-layer.psd', (100, 150)), ) BBOXES = ( ('1layer.psd', 0, BBox(0, 0, 101, 55)), ('2layers.psd', 0, BBox(8, 4, 93, 50)), ('2layers.psd', 1, BBox(0, 0, 101, 55)), ('group.psd', 0, BBox(25, 24, 66, 98)), ('empty-layer.psd', 0, BBox(36, 57, 52, 73)), ('empty-layer.psd', 1, BBox(0, 0, 100, 150)), ) RESOLUTIONS = ( ('1layer.psd', ResolutionInfo(h_res=72.0, h_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, v_res=72.0, v_res_unit=DisplayResolutionUnit.PIXELS_PER_INCH, width_unit=DimensionUnit.INCH, height_unit=DimensionUnit.INCH)),
def bbox(self): return BBox(0, 0, self.size[0], self.size[1])