| 224 | } |
| 225 | |
| 226 | func (v *Processor) newImageFromBlob( |
| 227 | ctx context.Context, blob *imagor.Blob, options *vips.LoadOptions, |
| 228 | ) (*vips.Image, error) { |
| 229 | if blob == nil || blob.IsEmpty() { |
| 230 | return nil, imagor.ErrNotFound |
| 231 | } |
| 232 | if blob.BlobType() == imagor.BlobTypeMemory { |
| 233 | buf, width, height, bands, _ := blob.Memory() |
| 234 | img, err := vips.NewImageFromMemory(buf, width, height, bands) |
| 235 | if err != nil { |
| 236 | return nil, err |
| 237 | } |
| 238 | // NewImageFromMemory assigns VIPS_INTERPRETATION_MULTIBAND by default. |
| 239 | // Cached raw-pixel blobs were normalized to sRGB on write, so restore |
| 240 | if bands >= 3 { |
| 241 | if copied, copyErr := img.Copy(&vips.CopyOptions{ |
| 242 | Interpretation: vips.InterpretationSrgb, |
| 243 | }); copyErr == nil { |
| 244 | img.Close() |
| 245 | return copied, nil |
| 246 | } |
| 247 | } |
| 248 | return img, nil |
| 249 | } |
| 250 | // Camera RAW files (RAF, ORF, RW2, X3F, CR3) must use dcrawload explicitly. |
| 251 | // CR2 is excluded: it is TIFF-based and crashes dcrawload_source, so it falls |
| 252 | // through to the normal TIFF loader below. |
| 253 | if blob.IsRaw() && blob.BlobType() != imagor.BlobTypeCR2 { |
| 254 | if !v.hasDcrawload { |
| 255 | return nil, imagor.ErrUnsupportedFormat |
| 256 | } |
| 257 | return v.dcrawloadFromBlob(ctx, blob) |
| 258 | } |
| 259 | // For TIFF blobs (ARW, NEF, DNG, PEF, SRW, NRW, CR2, regular TIFF), try dcrawload |
| 260 | // first when available — it handles TIFF-based RAW formats that share TIFF magic bytes. |
| 261 | // CR2 is now BlobTypeCR2 (not BlobTypeTIFF) so it won't hit this branch. |
| 262 | // LibRaw rejects non-RAW TIFFs quickly (header check only). |
| 263 | if blob.BlobType() == imagor.BlobTypeTIFF && v.hasDcrawload { |
| 264 | img, err := v.dcrawloadFromBlob(ctx, blob) |
| 265 | if err == nil { |
| 266 | return img, nil |
| 267 | } |
| 268 | // dcrawload failed — it's a real TIFF or unsupported, proceed with normal loading |
| 269 | } |
| 270 | src, err := v.newSourceFromBlob(ctx, blob) |
| 271 | if err != nil { |
| 272 | return nil, err |
| 273 | } |
| 274 | img, err := vips.NewImageFromSource(src, options) |
| 275 | if err != nil && v.FallbackFunc != nil { |
| 276 | src.Close() |
| 277 | return v.FallbackFunc(blob, options) |
| 278 | } |
| 279 | return img, err |
| 280 | } |
| 281 | |
| 282 | // dcrawloadFromBlob loads a RAW camera image using vips_dcrawload_source. |
| 283 | func (v *Processor) dcrawloadFromBlob(ctx context.Context, blob *imagor.Blob) (*vips.Image, error) { |