mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:25:49 +08:00
145 lines
4.2 KiB
Go
145 lines
4.2 KiB
Go
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
|
//
|
|
// 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.
|
|
|
|
package image
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
)
|
|
|
|
// StorageCost 预留十六位像素空间,并计入编码、调色板与底层像素缓冲容量
|
|
// 包装图像可实现BackingImage() image.Image,使底层共享缓冲计入预算
|
|
// 入参: img 图片资源, limit 缓存预算
|
|
// 返回: int 估算字节数或超预算标记
|
|
func StorageCost(img image.Image, limit int) int {
|
|
cost := 256
|
|
w, h := img.Bounds().Dx(), img.Bounds().Dy()
|
|
if limit < cost || w <= 0 || h <= 0 || w > (limit-cost)/8/h {
|
|
return limit + 1
|
|
}
|
|
pixels := w * h * 8
|
|
if encoded, ok := img.(*EncodedImage); ok {
|
|
if encoded.pngColor != nil {
|
|
if limit-cost < 512 {
|
|
return limit + 1
|
|
}
|
|
cost += 512
|
|
}
|
|
if cap(encoded.profile) > limit-cost {
|
|
return limit + 1
|
|
}
|
|
cost += cap(encoded.profile)
|
|
if encoded.profile != nil || encoded.pngColor != nil || encoded.config.ColorModel == color.CMYKModel {
|
|
if w > (limit-cost)/12/h {
|
|
return limit + 1
|
|
}
|
|
pixels = w * h * 12
|
|
}
|
|
if result := encoded.result.Load(); result != nil && result.image != nil {
|
|
pixels = max(pixels, StorageCost(result.image, limit-cost))
|
|
}
|
|
if cap(encoded.data) > limit-cost-pixels {
|
|
return limit + 1
|
|
}
|
|
return cost + pixels + cap(encoded.data)
|
|
}
|
|
var buffers [5]int
|
|
switch source := img.(type) {
|
|
case *colorImage:
|
|
buffers[0] = cap(source.Pix)
|
|
buffers[1] = StorageCost(source.source, limit-cost)
|
|
case *image.RGBA:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.RGBA64:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.NRGBA:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.NRGBA64:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.Gray:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.Gray16:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.Alpha:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.Alpha16:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.CMYK:
|
|
buffers[0] = cap(source.Pix)
|
|
case *image.Paletted:
|
|
if cap(source.Palette) > (limit-cost)/16 {
|
|
return limit + 1
|
|
}
|
|
buffers[0], buffers[1] = cap(source.Pix), cap(source.Palette)*16
|
|
case *image.YCbCr:
|
|
buffers = [5]int{cap(source.Y), cap(source.Cb), cap(source.Cr)}
|
|
case *image.NYCbCrA:
|
|
buffers = [5]int{cap(source.Y), cap(source.Cb), cap(source.Cr), cap(source.A)}
|
|
case *planarTIFFImage:
|
|
if cap(source.palette) > (limit-cost)/8 {
|
|
return limit + 1
|
|
}
|
|
cost += cap(source.palette) * 8
|
|
if cap(source.planes) > (limit-cost)/16 || len(source.planes) > len(buffers) {
|
|
return limit + 1
|
|
}
|
|
cost += cap(source.planes) * 16
|
|
for i, plane := range source.planes {
|
|
switch plane := plane.(type) {
|
|
case *image.Gray:
|
|
buffers[i] = cap(plane.Pix)
|
|
case *image.Gray16:
|
|
buffers[i] = cap(plane.Pix)
|
|
default:
|
|
buffers[i] = StorageCost(plane, limit-cost)
|
|
}
|
|
}
|
|
case *tiffFaxImage:
|
|
if cap(source.blocks) > (limit-cost)/16 {
|
|
return limit + 1
|
|
}
|
|
cost += cap(source.blocks) * 16
|
|
for _, block := range source.blocks {
|
|
size := StorageCost(block, limit-cost)
|
|
if size > limit-cost {
|
|
return limit + 1
|
|
}
|
|
cost += size
|
|
}
|
|
case *orientedTIFFImage:
|
|
buffers[0] = StorageCost(source.source, limit-cost)
|
|
case *maskedTIFFImage:
|
|
buffers[0] = StorageCost(source.source, limit-cost)
|
|
buffers[1] = StorageCost(source.mask, limit-cost)
|
|
case *transparentEdgeImage:
|
|
buffers[0] = StorageCost(source.source, limit-cost)
|
|
case *transparentEdgeImage64:
|
|
buffers[0] = StorageCost(source.source, limit-cost)
|
|
case interface{ BackingImage() image.Image }:
|
|
buffers[0] = StorageCost(source.BackingImage(), limit-cost)
|
|
}
|
|
storage := 0
|
|
for _, size := range buffers {
|
|
if size > limit-cost-storage {
|
|
return limit + 1
|
|
}
|
|
storage += size
|
|
}
|
|
if pixels > limit-cost {
|
|
return limit + 1
|
|
}
|
|
return cost + max(pixels, storage)
|
|
}
|