paint: import psd masks
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "iron_array.h"
|
||||
#include "iron_gpu.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -175,6 +176,9 @@ static uint8_t *psd_decode_channel(uint8_t *buf, size_t buf_size, size_t *pos, u
|
||||
|
||||
typedef struct {
|
||||
int32_t top, left, bottom, right;
|
||||
int32_t mask_top, mask_left, mask_bottom, mask_right;
|
||||
uint8_t mask_default_color;
|
||||
int has_mask;
|
||||
uint16_t num_channels;
|
||||
int16_t chan_ids[PSD_MAX_CHAN];
|
||||
uint64_t chan_lengths[PSD_MAX_CHAN]; // Includes 2-byte compression header
|
||||
@@ -314,7 +318,16 @@ void *io_psd_parse(uint8_t *buf, size_t buf_size, const char *file_name) {
|
||||
// Layer mask data
|
||||
if (pos + 4 <= buf_size) {
|
||||
uint32_t mask_len = psd_r32(buf, pos);
|
||||
pos += 4 + mask_len;
|
||||
pos += 4;
|
||||
if (mask_len >= 17 && pos + 17 <= buf_size) {
|
||||
layers[i].mask_top = (int32_t)psd_r32(buf, pos);
|
||||
layers[i].mask_left = (int32_t)psd_r32(buf, pos + 4);
|
||||
layers[i].mask_bottom = (int32_t)psd_r32(buf, pos + 8);
|
||||
layers[i].mask_right = (int32_t)psd_r32(buf, pos + 12);
|
||||
layers[i].mask_default_color = buf[pos + 16];
|
||||
layers[i].has_mask = 1;
|
||||
}
|
||||
pos += mask_len;
|
||||
}
|
||||
// Layer blending ranges
|
||||
if (pos + 4 <= buf_size) {
|
||||
@@ -353,11 +366,21 @@ void *io_psd_parse(uint8_t *buf, size_t buf_size, const char *file_name) {
|
||||
size_t row_bytes = (size_t)lw * depth_bytes;
|
||||
uint8_t *bufs[PSD_MAX_CHAN];
|
||||
memset(bufs, 0, sizeof(bufs));
|
||||
uint8_t *mask_buf = NULL;
|
||||
|
||||
int32_t mw = layers[i].has_mask ? (layers[i].mask_right - layers[i].mask_left) : lw;
|
||||
int32_t mh = layers[i].has_mask ? (layers[i].mask_bottom - layers[i].mask_top) : lh;
|
||||
int32_t mox = layers[i].has_mask ? layers[i].mask_left : layers[i].left;
|
||||
int32_t moy = layers[i].has_mask ? layers[i].mask_top : layers[i].top;
|
||||
size_t mask_row_bytes = (mw > 0 && mh > 0) ? (size_t)mw * depth_bytes : 0;
|
||||
|
||||
for (uint16_t c = 0; c < layers[i].num_channels; c++) {
|
||||
// Skip layer mask channels (-2, -3)
|
||||
if (layers[i].chan_ids[c] < -1) {
|
||||
pos += (size_t)layers[i].chan_lengths[c];
|
||||
// Decode first mask channel using mask bounds, skip additional ones
|
||||
if (mask_buf == NULL && mask_row_bytes > 0)
|
||||
mask_buf = psd_decode_channel(buf, buf_size, &pos, layers[i].chan_lengths[c], (uint32_t)mh, mask_row_bytes, version);
|
||||
else
|
||||
pos += (size_t)layers[i].chan_lengths[c];
|
||||
continue;
|
||||
}
|
||||
bufs[c] = psd_decode_channel(buf, buf_size, &pos, layers[i].chan_lengths[c], (uint32_t)lh, row_bytes, version);
|
||||
@@ -369,13 +392,16 @@ void *io_psd_parse(uint8_t *buf, size_t buf_size, const char *file_name) {
|
||||
for (uint16_t c = 0; c < layers[i].num_channels; c++)
|
||||
free(bufs[c]);
|
||||
|
||||
if (!layer_rgba)
|
||||
if (!layer_rgba) {
|
||||
free(mask_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Place the layer into a full-image canvas at its (left, top) offset
|
||||
uint8_t *rgba = (uint8_t *)calloc((size_t)width * height * 4, 1);
|
||||
if (!rgba) {
|
||||
free(layer_rgba);
|
||||
free(mask_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -404,14 +430,61 @@ void *io_psd_parse(uint8_t *buf, size_t buf_size, const char *file_name) {
|
||||
b->length = b->capacity = (uint32_t)((size_t)width * height * 4);
|
||||
void *tex = gpu_create_texture_from_bytes(b, (int)width, (int)height, GPU_TEXTURE_FORMAT_RGBA32);
|
||||
|
||||
char *layer_name = layers[i].name[0] != 0 ? layers[i].name : "layer";
|
||||
if (num_layers_found == 0) {
|
||||
result = tex;
|
||||
}
|
||||
else {
|
||||
char *layer_name = layers[i].name[0] != 0 ? layers[i].name : "layer";
|
||||
io_psd_import_layer((char *)file_name, layer_name, tex);
|
||||
}
|
||||
num_layers_found++;
|
||||
|
||||
// Import mask as a separate grayscale texture
|
||||
if (mask_buf != NULL) {
|
||||
uint8_t *mask_rgba = (uint8_t *)calloc((size_t)width * height * 4, 1);
|
||||
if (mask_rgba) {
|
||||
for (int32_t y = 0; y < mh; y++) {
|
||||
int32_t dy = moy + y;
|
||||
if (dy < 0 || dy >= (int32_t)height)
|
||||
continue;
|
||||
for (int32_t x = 0; x < mw; x++) {
|
||||
int32_t dx = mox + x;
|
||||
if (dx < 0 || dx >= (int32_t)width)
|
||||
continue;
|
||||
size_t src_i = ((size_t)y * mw + x) * depth_bytes;
|
||||
uint8_t v;
|
||||
if (depth == 8)
|
||||
v = mask_buf[src_i];
|
||||
else if (depth == 16)
|
||||
v = (uint8_t)(psd_r16(mask_buf, src_i) / 257);
|
||||
else {
|
||||
float fv = *(float *)(mask_buf + src_i);
|
||||
v = (uint8_t)((fv < 0.0f ? 0.0f : fv > 1.0f ? 1.0f : fv) * 255.0f);
|
||||
}
|
||||
uint8_t *dst = mask_rgba + ((size_t)dy * width + dx) * 4;
|
||||
if (layers[i].mask_default_color == 255) {
|
||||
// White background, dark stroke: invert so stroke is opaque, bg transparent
|
||||
dst[0] = dst[1] = dst[2] = 0;
|
||||
dst[3] = (uint8_t)(255 - v);
|
||||
}
|
||||
else {
|
||||
// Black background, light stroke
|
||||
dst[0] = dst[1] = dst[2] = 255;
|
||||
dst[3] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer_t *mb = (buffer_t *)malloc(sizeof(buffer_t));
|
||||
mb->buffer = mask_rgba;
|
||||
mb->length = mb->capacity = (uint32_t)((size_t)width * height * 4);
|
||||
void *mask_tex = gpu_create_texture_from_bytes(mb, (int)width, (int)height, GPU_TEXTURE_FORMAT_RGBA32);
|
||||
|
||||
char mask_name[256 + 5];
|
||||
snprintf(mask_name, sizeof(mask_name), "%s_mask", layer_name);
|
||||
io_psd_import_layer((char *)file_name, mask_name, mask_tex);
|
||||
}
|
||||
free(mask_buf);
|
||||
}
|
||||
}
|
||||
|
||||
free(layers);
|
||||
|
||||
Reference in New Issue
Block a user