void psd_layer_type_tool_free(psd_uint info_data) { psd_layer_type_tool * data; psd_int i; data = (psd_layer_type_tool *)info_data; for(i = 0; i < data->faces_count; i ++) psd_free(data->face[i].vector); psd_free(data->face); psd_free(data->style); psd_free(data->line); psd_free(data); }
void psd_layer_levels_free(psd_uint info_data) { psd_layer_levels * data; data = (psd_layer_levels *)info_data; psd_freeif(data->extra_record); psd_free(data); }
psd_status psd_image_free(psd_context * context) { if(context == NULL) return psd_status_invalid_context; psd_color_mode_data_free(context); psd_image_resource_free(context); psd_layer_and_mask_free(context); psd_image_data_free(context); psd_image_blend_free(context); psd_stream_free(context); psd_free(context); return psd_status_done; }
static psd_status psd_image_load_tag(psd_context ** dst_context, psd_char * file_name, psd_load_tag load_tag) { psd_context * context; psd_status status; if(dst_context == NULL) return psd_status_invalid_context; if(file_name == NULL) return psd_status_invalid_file; context = (psd_context *)psd_malloc(sizeof(psd_context)); if(context == NULL) return psd_status_malloc_failed; memset(context, 0, sizeof(psd_context)); context->ops_ = &psd_std_fileops; context->file_name = file_name; context->file = psd_fopen(file_name); if (context->file == NULL) { psd_free(context); return psd_status_invalid_file; } context->state = PSD_FILE_HEADER; context->stream.file_length = psd_fsize(context->file); context->load_tag = load_tag; status = psd_main_loop(context); if(status != psd_status_done) { psd_image_free(context); context = NULL; } else { psd_stream_free(context); } *dst_context = context; return status; }
int main (int argc, char **argv) { psd_context * context = NULL; psd_status status; if(argc <= 1) return -1; // parse the psd file to psd_context status = psd_image_load(&context, argv[1]); // use it... // // free if it's done psd_image_free(context); psd_free(context); return (status == psd_status_done ? 0 : -1); }
psd_status psd_layer_effects_blend_drop_shadow(psd_context * context, psd_layer_record * layer, psd_layer_effects * data) { psd_layer_effects_drop_shadow * drop_shadow = &data->drop_shadow; psd_int width, height; psd_int angle; psd_int distance_x, distance_y; psd_bitmap src_bmp, dst_bmp, knock_bmp; psd_layer_mask_info layer_mask_info; psd_int spread_size, blur_size; if(drop_shadow->use_global_light == psd_true) angle = context->global_angle; else angle = drop_shadow->angle; distance_x = -(psd_int)(drop_shadow->distance * cos(PSD_PI * angle / 180) + 0.5); distance_y = (psd_int)(drop_shadow->distance * sin(PSD_PI * angle / 180) + 0.5); data->left[psd_layer_effects_type_drop_shadow] = -drop_shadow->size + distance_x; data->top[psd_layer_effects_type_drop_shadow] = -drop_shadow->size + distance_y; width = layer->width + drop_shadow->size * 2; height = layer->height + drop_shadow->size * 2; data->right[psd_layer_effects_type_drop_shadow] = data->left[psd_layer_effects_type_drop_shadow] + width; data->bottom[psd_layer_effects_type_drop_shadow] = data->top[psd_layer_effects_type_drop_shadow] + height; data->blend_mode[psd_layer_effects_type_drop_shadow] = drop_shadow->blend_mode; data->opacity[psd_layer_effects_type_drop_shadow] = drop_shadow->opacity; if(data->image_data[psd_layer_effects_type_drop_shadow] != NULL) { if(data->width[psd_layer_effects_type_drop_shadow] != width || data->height[psd_layer_effects_type_drop_shadow] != height) { psd_free(data->image_data[psd_layer_effects_type_drop_shadow]); data->image_data[psd_layer_effects_type_drop_shadow] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_drop_shadow] == NULL) return psd_status_malloc_failed; } } else { data->image_data[psd_layer_effects_type_drop_shadow] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_drop_shadow] == NULL) return psd_status_malloc_failed; } data->width[psd_layer_effects_type_drop_shadow] = width; data->height[psd_layer_effects_type_drop_shadow] = height; psd_color_memset(data->image_data[psd_layer_effects_type_drop_shadow], drop_shadow->color, width * height); src_bmp.width = layer->width; src_bmp.height = layer->height; src_bmp.image_data = layer->image_data; dst_bmp.width = width; dst_bmp.height = height; dst_bmp.image_data = data->image_data[psd_layer_effects_type_drop_shadow]; if(drop_shadow->size == 0) { psd_fill_bitmap(&dst_bmp, drop_shadow->color); psd_bitmap_copy_alpha_channel(&dst_bmp, &src_bmp); } else { psd_inflate_bitmap(&dst_bmp, &src_bmp, drop_shadow->size, drop_shadow->size); psd_fill_bitmap_without_alpha_channel(&dst_bmp, drop_shadow->color); } memcpy(&layer_mask_info, &layer->layer_mask_info, sizeof(psd_layer_mask_info)); if(layer_mask_info.disabled == psd_false && (layer_mask_info.default_color != 255 || layer_mask_info.mask_data != NULL)) { layer_mask_info.left -= layer->left - drop_shadow->size; layer_mask_info.top -= layer->top - drop_shadow->size; layer_mask_info.right -= layer->left - drop_shadow->size; layer_mask_info.bottom -= layer->top - drop_shadow->size; psd_bitmap_blend_mask(&dst_bmp, &layer_mask_info); } if(drop_shadow->knocks_out == psd_true) { psd_create_bitmap(&knock_bmp, width, height); psd_copy_bitmap(&knock_bmp, &dst_bmp); psd_offset_bitmap(&knock_bmp, distance_x, distance_y, psd_color_clear); } spread_size = (drop_shadow->spread * drop_shadow->size + 50) / 100; blur_size = drop_shadow->size - spread_size; if(spread_size != 0) { psd_bitmap_gaussian_blur_alpha_channel(&dst_bmp, spread_size); psd_bitmap_find_edge(&dst_bmp, psd_true); } if(blur_size != 0) { psd_bitmap_gaussian_blur_alpha_channel(&dst_bmp, blur_size); } psd_bitmap_contour_alpha_channel(&dst_bmp, drop_shadow->contour_lookup_table, drop_shadow->anti_aliased, psd_true); if(drop_shadow->noise > 0) { psd_effects_add_noise(&dst_bmp, drop_shadow->noise, data->left[psd_layer_effects_type_drop_shadow] + layer->left, data->top[psd_layer_effects_type_drop_shadow] + layer->top, context); } if(drop_shadow->knocks_out == psd_true) { psd_bitmap_knock_out(&dst_bmp, &knock_bmp); psd_free_bitmap(&knock_bmp); } data->valid[psd_layer_effects_type_drop_shadow] = psd_false; return psd_status_done; }
void psd_freeif(void * block) { if (block != NULL) psd_free(block); }
void psd_bitmap_gaussian_blur_alpha_channel(psd_bitmap * bitmap, psd_double radius) { psd_int width, height; psd_int *buf, *bb; psd_int pixels; psd_int total = 1, total2; psd_int i, row, col; psd_int start, end; psd_int *curve; psd_int *sum = NULL; psd_int val; psd_int length; psd_int initial_p, initial_m; psd_double std_dev; psd_color_component * src, * sp; psd_argb_color * src_data, * dst_data; if(radius <= 0.0) return; width = bitmap->width; height = bitmap->height; if(width < 1 || height < 1) return; buf = (psd_int *)psd_malloc(PSD_MAX(width, height) * 2 * sizeof(psd_int)); src = (psd_color_component *)psd_malloc(PSD_MAX(width, height)); /* First the vertical pass */ radius = fabs(radius) * 1.4; std_dev = sqrt(-(radius * radius) / (2 * log(1.0 / 255.0))); curve = psd_make_curve (std_dev, &length); sum = (psd_int *)psd_malloc((2 * length + 1) * sizeof(psd_int)); sum[0] = 0; for(i = 1; i <= length*2; i++) sum[i] = curve[i-length-1] + sum[i-1]; sum += length; total = sum[length] - sum[-length]; total2 = total / 2; /* First, the vertical pass */ for(col = 0; col < width; col++) { for(row = 0, src_data = bitmap->image_data + width + col; row < height - 1; row ++, src_data += width) src[row] = PSD_GET_ALPHA_COMPONENT(*src_data); src[height - 1] = 0; sp = src; initial_p = *sp; initial_m = *(sp + height - 1); /* Determine a run-length encoded version of the row */ psd_run_length_encode(sp, buf, height); for(row = 0, dst_data = bitmap->image_data + col; row < height; row ++, dst_data += width) { start = (row < length) ? -row : -length; end = (height <= (row + length) ? (height - row - 1) : length); val = 0; i = start; bb = buf + (row + i) * 2; if(start != -length) val += initial_p * (sum[start] - sum[-length]); while(i < end) { pixels = bb[0]; i += pixels; if(i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if(end != length) val += initial_m * (sum[length] - sum[end]); *dst_data = (*dst_data & 0x00FFFFFF) | ((val + total2) / total << 24); } } /* Now the horizontal pass */ for(row = 0; row < height; row++) { for(col = 0, src_data = bitmap->image_data + row * width + 1; col < width - 1; col ++, src_data ++) src[col] = PSD_GET_ALPHA_COMPONENT(*src_data); src[width - 1] = 0; sp = src; initial_p = *sp; initial_m = *(sp + width - 1); /* Determine a run-length encoded version of the row */ psd_run_length_encode(sp, buf, width); for(col = 0, dst_data = bitmap->image_data + row * width; col < width; col ++, dst_data ++) { start = (col < length) ? -col : -length; end = (width <= (col + length)) ? (width - col - 1) : length; val = 0; i = start; bb = buf + (col + i) * 2; if(start != -length) val += initial_p * (sum[start] - sum[-length]); while(i < end) { pixels = bb[0]; i += pixels; if(i > end) i = end; val += bb[1] * (sum[i] - sum[start]); bb += (pixels * 2); start = i; } if(end != length) val += initial_m * (sum[length] - sum[end]); *dst_data = (*dst_data & 0x00FFFFFF) | ((val + total2) / total << 24); } } /* free buffers */ psd_free(curve - length); psd_free(sum - length); psd_free(buf); psd_free(src); }
psd_status psd_layer_effects_blend_stroke(psd_context * context, psd_layer_record * layer, psd_layer_effects * data) { psd_layer_effects_stroke * stroke = &data->stroke; psd_int i, width, height, center_x, center_y, radius_x, radius_y, radius_corner; psd_int corner_angle, angle; psd_int sign_x = 1, sign_y = 1; psd_bitmap layer_bmp, src_bmp, dst_bmp; psd_layer_mask_info layer_mask_info; width = layer->width + stroke->size * 2; height = layer->height + stroke->size * 2; data->left[psd_layer_effects_type_stroke] = -stroke->size; data->top[psd_layer_effects_type_stroke] = -stroke->size; data->right[psd_layer_effects_type_stroke] = layer->width + stroke->size; data->bottom[psd_layer_effects_type_stroke] = layer->height + stroke->size; data->blend_mode[psd_layer_effects_type_stroke] = stroke->blend_mode; data->opacity[psd_layer_effects_type_stroke] = stroke->opacity; if(data->image_data[psd_layer_effects_type_stroke] != NULL) { if(data->width[psd_layer_effects_type_stroke] != width || data->height[psd_layer_effects_type_stroke] != height) { psd_free(data->image_data[psd_layer_effects_type_stroke]); data->image_data[psd_layer_effects_type_stroke] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_stroke] == NULL) return psd_status_malloc_failed; } } else { data->image_data[psd_layer_effects_type_stroke] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_stroke] == NULL) return psd_status_malloc_failed; } data->width[psd_layer_effects_type_stroke] = width; data->height[psd_layer_effects_type_stroke] = height; layer_bmp.width = layer->width; layer_bmp.height = layer->height; layer_bmp.image_data = layer->image_data; psd_create_bitmap(&src_bmp, width, height); psd_inflate_bitmap(&src_bmp, &layer_bmp, stroke->size, stroke->size); dst_bmp.width = width; dst_bmp.height = height; dst_bmp.image_data = data->image_data[psd_layer_effects_type_stroke]; psd_fill_bitmap(&dst_bmp, psd_color_clear); switch(stroke->position) { case psd_stroke_outside: psd_draw_stroke(&dst_bmp, &src_bmp, stroke->size * 2); psd_bitmap_find_edge(&src_bmp, psd_true); psd_bitmap_knock_out(&dst_bmp, &src_bmp); break; case psd_stroke_inside: psd_draw_stroke(&dst_bmp, &src_bmp, stroke->size * 2); psd_bitmap_find_edge(&src_bmp, psd_true); psd_bitmap_reverse_alpha_channel(&src_bmp); psd_bitmap_knock_out(&dst_bmp, &src_bmp); break; case psd_stroke_center: psd_draw_stroke(&dst_bmp, &src_bmp, stroke->size); break; default: psd_assert(0); break; } psd_inflate_bitmap(&src_bmp, &layer_bmp, stroke->size, stroke->size); memcpy(&layer_mask_info, &layer->layer_mask_info, sizeof(psd_layer_mask_info)); if(layer_mask_info.disabled == psd_false && (layer_mask_info.default_color != 255 || layer_mask_info.mask_data != NULL)) { layer_mask_info.left -= layer->left - stroke->size; layer_mask_info.top -= layer->top - stroke->size; layer_mask_info.right -= layer->left - stroke->size; layer_mask_info.bottom -= layer->top - stroke->size; psd_bitmap_blend_mask(&src_bmp, &layer_mask_info); } psd_bitmap_reverse_mixed_alpha_channel(&src_bmp); psd_bitmap_blend_alpha_channel(&dst_bmp, &src_bmp); switch(stroke->fill_type) { case psd_fill_solid_color: psd_fill_bitmap_without_alpha_channel(&dst_bmp, stroke->fill_color); break; case psd_fill_gradient: if(stroke->gradient_align == psd_false) { center_x = context->width / 2 + stroke->gradient_horz_offset * context->width / 100; center_y = context->height / 2 + stroke->gradient_vert_offset * context->height / 100; center_x -= layer->left; center_y -= layer->top; corner_angle = (psd_int)(atan((psd_float)context->height / context->width) * 180 / PSD_PI + 0.5); angle = stroke->gradient_angle; width = (context->width * stroke->gradient_scale + 100) / 200; height = (context->height * stroke->gradient_scale + 100) / 200; } else { center_x = width / 2 + stroke->gradient_horz_offset * layer->width / 100; center_y = height / 2 + stroke->gradient_vert_offset * layer->height / 100; switch(stroke->position) { case psd_stroke_outside: width = layer->width + stroke->size * 2; height = layer->height + stroke->size * 2; break; case psd_stroke_inside: width = layer->width; height = layer->height; break; case psd_stroke_center: width = layer->width + stroke->size; height = layer->height + stroke->size; break; } center_x += stroke->gradient_horz_offset * width / 100; center_y += stroke->gradient_vert_offset * height / 100; corner_angle = (psd_int)(atan((psd_float)height / width) * 180 / PSD_PI + 0.5); angle = stroke->gradient_angle; width = (width * stroke->gradient_scale + 100) / 200; height = (height * stroke->gradient_scale + 100) / 200; } if(angle < 0) angle += 360; if(angle >= 90 && angle < 180) { angle = 180 - angle; sign_x = -1; } else if(angle >= 180 && angle < 270) { angle = angle - 180; sign_x = -1; sign_y = -1; } else if(angle >= 270 && angle <= 360) { angle = 360 - angle; sign_y = -1; } if(angle <= corner_angle) { radius_x = width; radius_y = (psd_int)(radius_x * PSD_TAN(angle) + 0.5); } else { radius_y = height; radius_x = (psd_int)(radius_y / PSD_TAN(angle) + 0.5); } radius_corner = (psd_int)(psd_carm_sqrt((psd_float)(radius_x * radius_x + radius_y * radius_y)) + 0.5); switch(stroke->gradient_style) { case psd_gradient_style_linear: psd_gradient_fill_linear(&src_bmp, &stroke->gradient_color, stroke->gradient_reverse, center_x - sign_x * radius_x, center_y + sign_y * radius_y, center_x + sign_x * radius_x, center_y - sign_y * radius_y); break; case psd_gradient_style_radial: psd_gradient_fill_radial(&src_bmp, &stroke->gradient_color, stroke->gradient_reverse, center_x, center_y, radius_corner); break; case psd_gradient_style_angle: psd_gradient_fill_angle(&src_bmp, &stroke->gradient_color, stroke->gradient_reverse, center_x, center_y, stroke->gradient_angle); break; case psd_gradient_style_reflected: psd_gradient_fill_reflected(&src_bmp, &stroke->gradient_color, stroke->gradient_reverse, center_x - sign_x * radius_x, center_y + sign_y * radius_y, center_x + sign_x * radius_x, center_y - sign_y * radius_y); break; case psd_gradient_style_diamond: psd_gradient_fill_diamond(&src_bmp, &stroke->gradient_color, stroke->gradient_reverse, center_x, center_y, radius_corner, stroke->gradient_angle); break; default: psd_assert(0); break; } psd_bitmap_copy_without_alpha_channel(&dst_bmp, &src_bmp); break; case psd_fill_pattern: for(i = 0; i < context->pattern_count; i ++) { if(strcmp(context->patterns[i].unique_id, stroke->pattern_info.identifier) == 0) { psd_pattern_fill(&src_bmp, &context->patterns[i], stroke->pattern_scale, stroke->pattern_horz_phase + stroke->size, stroke->pattern_vert_phase + stroke->size); break; } } psd_bitmap_copy_without_alpha_channel(&dst_bmp, &src_bmp); break; default: psd_assert(0); break; } psd_free_bitmap(&src_bmp); data->valid[psd_layer_effects_type_stroke] = psd_false; return psd_status_done; }
psd_status psd_layer_effects_blend_inner_glow(psd_context * context, psd_layer_record * layer, psd_layer_effects * data) { psd_layer_effects_inner_glow * inner_glow = &data->inner_glow; psd_int width, height; psd_bitmap src_bmp, dst_bmp, knock_bmp; psd_layer_mask_info layer_mask_info; psd_int choke_size, blur_size; psd_argb_color gradient_table[256]; data->left[psd_layer_effects_type_inner_glow] = -inner_glow->size; data->top[psd_layer_effects_type_inner_glow] = -inner_glow->size; width = layer->width + inner_glow->size * 2; height = layer->height + inner_glow->size * 2; data->right[psd_layer_effects_type_inner_glow] = data->left[psd_layer_effects_type_inner_glow] + width; data->bottom[psd_layer_effects_type_inner_glow] = data->top[psd_layer_effects_type_inner_glow] + height; data->blend_mode[psd_layer_effects_type_inner_glow] = inner_glow->blend_mode; data->opacity[psd_layer_effects_type_inner_glow] = inner_glow->opacity; if(data->image_data[psd_layer_effects_type_inner_glow] != NULL) { if(data->width[psd_layer_effects_type_inner_glow] != width || data->height[psd_layer_effects_type_inner_glow] != height) { psd_free(data->image_data[psd_layer_effects_type_inner_glow]); data->image_data[psd_layer_effects_type_inner_glow] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_inner_glow] == NULL) return psd_status_malloc_failed; } } else { data->image_data[psd_layer_effects_type_inner_glow] = (psd_argb_color *)psd_malloc(width * height * 4); if(data->image_data[psd_layer_effects_type_inner_glow] == NULL) return psd_status_malloc_failed; } data->width[psd_layer_effects_type_inner_glow] = width; data->height[psd_layer_effects_type_inner_glow] = height; psd_color_memset(data->image_data[psd_layer_effects_type_inner_glow], inner_glow->color, width * height); src_bmp.width = layer->width; src_bmp.height = layer->height; src_bmp.image_data = layer->image_data; dst_bmp.width = width; dst_bmp.height = height; dst_bmp.image_data = data->image_data[psd_layer_effects_type_inner_glow]; if(inner_glow->size == 0) { psd_fill_bitmap(&dst_bmp, inner_glow->color); psd_bitmap_copy_alpha_channel(&dst_bmp, &src_bmp); } else { psd_inflate_bitmap(&dst_bmp, &src_bmp, inner_glow->size, inner_glow->size); psd_fill_bitmap_without_alpha_channel(&dst_bmp, inner_glow->color); } memcpy(&layer_mask_info, &layer->layer_mask_info, sizeof(psd_layer_mask_info)); if(layer_mask_info.disabled == psd_false && (layer_mask_info.default_color != 255 || layer_mask_info.mask_data != NULL)) { layer_mask_info.left -= layer->left - inner_glow->size; layer_mask_info.top -= layer->top - inner_glow->size; layer_mask_info.right -= layer->left - inner_glow->size; layer_mask_info.bottom -= layer->top - inner_glow->size; psd_bitmap_blend_mask(&dst_bmp, &layer_mask_info); } psd_bitmap_reverse_alpha_channel(&dst_bmp); psd_create_bitmap(&knock_bmp, width, height); psd_copy_bitmap(&knock_bmp, &dst_bmp); if(inner_glow->technique == psd_technique_precise) psd_bitmap_find_edge(&dst_bmp, psd_false); choke_size = (inner_glow->choke * inner_glow->size + 50) / 100; blur_size = inner_glow->size - choke_size; if(choke_size != 0) { psd_bitmap_gaussian_blur_alpha_channel(&dst_bmp, choke_size); psd_bitmap_find_edge(&dst_bmp, psd_false); } if(blur_size != 0) { psd_bitmap_gaussian_blur_alpha_channel(&dst_bmp, blur_size); } psd_bitmap_ajust_range(&dst_bmp, inner_glow->range); if(inner_glow->source == psd_glow_center) psd_bitmap_reverse_alpha_channel(&dst_bmp); psd_bitmap_contour_alpha_channel(&dst_bmp, inner_glow->contour_lookup_table, inner_glow->anti_aliased, psd_false); if(inner_glow->noise > 0) { psd_effects_add_noise(&dst_bmp, inner_glow->noise, data->left[psd_layer_effects_type_inner_glow] + layer->left, data->top[psd_layer_effects_type_inner_glow] + layer->top, context); } if(inner_glow->fill_type == psd_fill_gradient) { psd_gradient_color_get_table(&inner_glow->gradient_color, gradient_table, 256, psd_false); psd_effects_apply_gradient(&dst_bmp, gradient_table, psd_false, inner_glow->jitter, data->left[psd_layer_effects_type_inner_glow] + layer->left, data->top[psd_layer_effects_type_inner_glow] + layer->top, context); } psd_bitmap_knock_out(&dst_bmp, &knock_bmp); psd_free_bitmap(&knock_bmp); data->valid[psd_layer_effects_type_inner_glow] = psd_false; return psd_status_done; }
psd_status psd_get_image_resource(psd_context * context) { psd_int length, i, size; psd_ushort ID; psd_uint tag; psd_uchar sizeofname; psd_int sizeofdata, prev_stream_pos; psd_uchar * buffer; //psd_status status; // Length of image resource section length = psd_stream_get_int(context); if(length <= 0) return psd_status_done; // default context->global_angle = 30; context->global_altitude = 30; while(length > 0) { // Signature: '8BIM' tag = psd_stream_get_int(context); if(tag == '8BIM') { length -= 4; // Unique identifier for the resource ID = psd_stream_get_short(context); length -= 2; // Name: Pascal string, padded to make the size even (a null name consists of two bytes of 0) sizeofname = psd_stream_get_char(context); if((sizeofname & 0x01) == 0) sizeofname ++; psd_stream_get_null(context, sizeofname); length -= sizeofname + 1; // Actual size of resource data that follows sizeofdata = psd_stream_get_int(context); length -= 4; // resource data must be even if(sizeofdata & 0x01) sizeofdata ++; length -= sizeofdata; switch(context->load_tag) { case psd_load_tag_thumbnail: if(ID != 1033 && ID != 1036) { psd_stream_get_null(context, sizeofdata); continue; } break; case psd_load_tag_merged: // alpha channels information if(ID != 1006 && ID != 1045 && ID != 1053) { psd_stream_get_null(context, sizeofdata); continue; } break; case psd_load_tag_exif: if(ID != 1058 && ID != 1059) { psd_stream_get_null(context, sizeofdata); continue; } break; } prev_stream_pos = context->stream.current_pos; if(sizeofdata > 0) { switch(ID) { // ResolutionInfo structure case 1005: // Horizontal resolution in pixels per inch. context->resolution_info.hres = psd_stream_get_int(context) / 65536.0f; // 1=display horitzontal resolution in pixels per inch; 2=display horitzontal resolution in pixels per cm. context->resolution_info.hres_unit = psd_stream_get_short(context); // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. context->resolution_info.width_unit = psd_stream_get_short(context); // Vertial resolution in pixels per inch. context->resolution_info.vres = psd_stream_get_int(context) / 65536.0f; // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm. context->resolution_info.vres_unit = psd_stream_get_short(context); // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. context->resolution_info.height_unit = psd_stream_get_short(context); context->fill_resolution_info = psd_true; break; // Names of the alpha channels as a series of Pascal strings. case 1006: buffer = (psd_uchar *)psd_malloc(sizeofdata); if(buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); if(context->alpha_channels == 0) { size = 0; // maybe odd while(size + 1 < sizeofdata) { size += *(buffer + size) + 1; context->alpha_channels ++; } context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } size = 0; for(i = 0; i < context->alpha_channels; i ++) { memcpy(context->alpha_channel_info[i].name, buffer + size + 1, *(buffer + size)); size += *(buffer + size) + 1; } psd_free(buffer); context->fill_alpha_channel_info = psd_true; break; // DisplayInfo structure case 1007: context->display_info.color = psd_stream_get_space_color(context); // 0..100 context->display_info.opacity = psd_stream_get_short(context); psd_assert(context->display_info.opacity >= 0 && context->display_info.opacity <= 100); // selected = 0, protected = 1 context->display_info.kind = psd_stream_get_char(context); // maybe be 2 when color mode is multichannel //psd_assert(context->display_info.kind == 0 || context->display_info.kind == 1); // padding psd_stream_get_char(context); context->fill_display_info = psd_true; break; // The caption as a Pascal string. case 1008: size = psd_stream_get_char(context); psd_stream_get(context, context->caption, size); break; // Layer state information // 2 bytes containing the index of target layer (0 = bottom layer). case 1024: context->target_layer_index = psd_stream_get_short(context); break; // Layers group information // 2 bytes per layer containing a group ID for the dragging groups. Layers in // a group have the same group ID. case 1026: context->layer_group_count = sizeofdata / 2; context->layer_group_id = (psd_ushort *)psd_malloc(context->layer_group_count * 2); if(context->layer_group_id == NULL) return psd_status_malloc_failed; for(i = 0; i < context->layer_group_count; i ++) context->layer_group_id[i] = psd_stream_get_short(context); context->fill_layer_group = psd_true; break; // (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only case 1033: // (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) case 1036: if(context->load_tag == psd_load_tag_layer) { psd_stream_get_null(context, sizeofdata); continue; } // 1 = kJpegRGB . Also supports kRawRGB (0). context->thumbnail_resource.format = psd_stream_get_int(context); psd_assert(context->thumbnail_resource.format == 0 || context->thumbnail_resource.format == 1); // Width of thumbnail in pixels. context->thumbnail_resource.width = psd_stream_get_int(context); // Height of thumbnail in pixels. context->thumbnail_resource.height = psd_stream_get_int(context); // Padded row bytes = (width * bits per pixel + 31) / 32 * 4. context->thumbnail_resource.width_bytes = psd_stream_get_int(context); // Total size = widthbytes * height * planes context->thumbnail_resource.total_size = psd_stream_get_int(context); // Used for consistency check. context->thumbnail_resource.size_after_compression = psd_stream_get_int(context); context->thumbnail_resource.bits_per_pixel = psd_stream_get_short(context); // Bits per pixel. = 24 psd_assert(context->thumbnail_resource.bits_per_pixel == 24); context->thumbnail_resource.number_of_planes = psd_stream_get_short(context); // Number of planes. = 1 psd_assert(context->thumbnail_resource.number_of_planes == 1); #ifdef PSD_INCLUDE_LIBJPEG if(context->thumbnail_resource.format == 0) { status = psd_thumbnail_decode_raw(&context->thumbnail_resource.thumbnail_data, context->thumbnail_resource.size_after_compression, context); } else { status = psd_thumbnail_decode_jpeg(&context->thumbnail_resource.thumbnail_data, context->thumbnail_resource.size_after_compression, context); } if(status != psd_status_done) return status; #else context->thumbnail_resource.jfif_data = (psd_uchar *)psd_malloc(sizeofdata - 28); if(context->thumbnail_resource.jfif_data == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->thumbnail_resource.jfif_data, sizeofdata - 28); #endif context->fill_thumbnail_resource = psd_true; break; // (Photoshop 4.0) Copyright flag // Boolean indicating whether image is copyrighted. Can be set via // Property suite or by user in File Info... case 1034: context->copyright_flag = (psd_bool)psd_stream_get_short(context); psd_assert(context->copyright_flag == 0 || context->copyright_flag == 1); break; // (Photoshop 5.0) Global Angle // 4 bytes that contain an integer between 0 and 359, which is the global // lighting angle for effects layer. If not present, assumed to be 30. case 1037: context->global_angle = psd_stream_get_int(context); break; // (Photoshop 5.0) Effects visible // 1-byte global flag to show/hide all the effects layer. Only present when // they are hidden. case 1042: context->effects_visible = (psd_bool)psd_stream_get_short(context); psd_assert(context->effects_visible == 0 || context->effects_visible == 1); break; // (Photoshop 5.0) Unicode Alpha Names // Unicode string (4 bytes length followed by string). case 1045: buffer = (psd_uchar *)psd_malloc(sizeofdata); if(buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); if(context->alpha_channels == 0) { size = 0; while(size < sizeofdata) { size += PSD_CHAR_TO_INT(buffer + size) * 2 + 4; context->alpha_channels ++; } context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } size = 0; for(i = 0; i < context->alpha_channels; i ++) { context->alpha_channel_info[i].unicode_name_length = PSD_CHAR_TO_INT(buffer + size); context->alpha_channel_info[i].unicode_name = (psd_ushort *)psd_malloc(2 * context->alpha_channel_info[i].unicode_name_length); if(context->alpha_channel_info[i].unicode_name == NULL) { psd_free(buffer); return psd_status_malloc_failed; } memcpy(context->alpha_channel_info[i].unicode_name, buffer + size + 4, 2 * context->alpha_channel_info[i].unicode_name_length); size += 2 * context->alpha_channel_info[i].unicode_name_length + 4; } psd_free(buffer); context->fill_alpha_channel_info = psd_true; break; // (Photoshop 6.0) Indexed Color Table Count // 2 bytes for the number of colors in table that are actually defined case 1046: context->indexed_color_table_count = psd_stream_get_short(context); break; // (Photoshop 6.0) Transparency Index. // 2 bytes for the index of transparent color, if any. case 1047: context->transparency_index = psd_stream_get_short(context); break; // (Photoshop 6.0) Global Altitude // 4 byte entry for altitude case 1049: context->global_altitude = psd_stream_get_int(context); break; // (Photoshop 6.0) Alpha Identifiers // 4 bytes of length, followed by 4 bytes each for every alpha identifier. case 1053: if(context->alpha_channels == 0) { context->alpha_channels = sizeofdata / 4; context->color_channels = context->channels - context->alpha_channels; context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info)); if(context->alpha_channel_info == NULL) return psd_status_malloc_failed; memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info)); } for(i = 0; i < context->alpha_channels; i ++) { context->alpha_channel_info[i].identifier = psd_stream_get_int(context); } context->fill_alpha_channel_info = psd_true; break; // (Photoshop 6.0) Version Info // 4 bytes version, 1 byte hasRealMergedData, Unicode string: writer // name, Unicode string: reader name, 4 bytes file version. case 1057: context->version_info.version = psd_stream_get_int(context); context->version_info.has_real_merged_data = psd_stream_get_bool(context); context->version_info.writer_name_length = psd_stream_get_int(context); context->version_info.writer_name = (psd_ushort *)psd_malloc(2 * context->version_info.writer_name_length); if(context->version_info.writer_name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->version_info.writer_name, 2 * context->version_info.writer_name_length); context->version_info.reader_name_length = psd_stream_get_int(context); context->version_info.reader_name = (psd_ushort *)psd_malloc(2 * context->version_info.reader_name_length); if(context->version_info.reader_name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->version_info.reader_name, 2 * context->version_info.reader_name_length); context->version_info.file_version = psd_stream_get_int(context); context->fill_version_info = psd_true; break; // if you don't need the following image resource, // you can undef this macro in psd_config.h to reduce the parsing time ///////////////////////////////////////////////////////////////////////////////////////////// #ifdef PSD_GET_ALL_IMAGE_RESOURCE // Border information case 1009: // a fixed number (2 bytes real, 2 bytes fraction) for the border width context->border_info.border_width = psd_fixed_16_16_tofloat((psd_fixed_16_16)psd_stream_get_int(context)); // 2 bytes for border units (1 = inches, 2 = cm, 3 = points, 4 = picas, 5 = columns). context->border_info.border_units = (psd_units)psd_stream_get_short(context); psd_assert(context->border_info.border_units >= psd_units_inches && context->border_info.border_units <= psd_units_columns); context->fill_border_info = psd_true; break; // Print flags // A series of one-byte boolean values (see Page Setup dialog): labels, crop // marks, color bars, registration marks, negative, flip, interpolate, caption, // print flags. case 1011: context->print_flags.labels = psd_stream_get_bool(context); context->print_flags.crop_marks = psd_stream_get_bool(context); context->print_flags.color_bars = psd_stream_get_bool(context); context->print_flags.registration_marks = psd_stream_get_bool(context); context->print_flags.negative = psd_stream_get_bool(context); context->print_flags.flip = psd_stream_get_bool(context); context->print_flags.interpolate = psd_stream_get_bool(context); context->print_flags.caption = psd_stream_get_bool(context); context->print_flags.print_flags = psd_stream_get_bool(context); context->fill_print_flags = psd_true; break; // (Photoshop 4.0) Grid and guides information case 1032: // Version ( = 1) psd_assert(psd_stream_get_int(context) == 1); // Future implementation of document-specific grids (4 bytes horizontal, 4 bytes vertical). context->grid_guides.horz_grid = psd_stream_get_int(context); context->grid_guides.vert_grid = psd_stream_get_int(context); // Number of guide resource blocks (can be 0). context->grid_guides.guide_count = psd_stream_get_int(context); if(context->grid_guides.guide_count > 0) { //Location of guide in document coordinates. Since the guide is either vertical or horizontal, this only has to be one component of the coordinate. context->grid_guides.guide_coordinate = (psd_int *)psd_malloc(context->grid_guides.guide_count * 4); // Direction of guide. VHSelect is a system type of psd_uchar where 0 = vertical, 1 = horizontal. context->grid_guides.guide_direction = (psd_uchar *)psd_malloc(context->grid_guides.guide_count); if(context->grid_guides.guide_coordinate == NULL || context->grid_guides.guide_direction == NULL) { psd_free(context->grid_guides.guide_coordinate); context->grid_guides.guide_coordinate = NULL; psd_free(context->grid_guides.guide_direction); context->grid_guides.guide_direction = NULL; return psd_status_malloc_failed; } for(i = 0; i < context->grid_guides.guide_count; i ++) { context->grid_guides.guide_coordinate[i] = psd_stream_get_int(context); context->grid_guides.guide_direction[i] = psd_stream_get_char(context); } } context->fill_grid_and_guides_info = psd_true; break; // (Photoshop 5.0) Color samplers resource case 1038: // Version ( = 1) psd_assert(psd_stream_get_int(context) == 1); // Number of color samplers to follow. context->color_samplers.number_of_color_samplers = psd_stream_get_int(context); if (context->color_samplers.number_of_color_samplers > 0) { context->color_samplers.resource = (psd_color_samplers_resource *) psd_malloc(sizeof(psd_color_samplers_resource) * context->color_samplers.number_of_color_samplers); if (context->color_samplers.resource == NULL) return psd_status_malloc_failed; for (i = 0; i < context->color_samplers.number_of_color_samplers; i ++) { // The vertical and horizontal position of the point (4 bytes each). context->color_samplers.resource[i].vertical_position = psd_stream_get_int(context); context->color_samplers.resource[i].horizontal_position = psd_stream_get_int(context); // Color Space context->color_samplers.resource[i].color_space = psd_stream_get_short(context); } } context->fill_color_samplers = psd_true; break; // (Photoshop 6.0) Slices case 1050: // Version ( = 6) psd_assert(psd_stream_get_int(context) == 6); // Bounding rectangle for all of the slices: top, left, bottom, right of all the slices context->slices_resource.bounding_top = psd_stream_get_int(context); context->slices_resource.bounding_left = psd_stream_get_int(context); context->slices_resource.bounding_bottom = psd_stream_get_int(context); context->slices_resource.bounding_right = psd_stream_get_int(context); // Name of group of slices: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Number of slices to follow context->slices_resource.number_of_slices = psd_stream_get_int(context); context->slices_resource.slices_resource_block = (psd_slices_resource_block *)psd_malloc(context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block)); if(context->slices_resource.slices_resource_block == NULL) return psd_status_malloc_failed; memset(context->slices_resource.slices_resource_block, 0, context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block)); for(i = 0; i < context->slices_resource.number_of_slices; i ++) { context->slices_resource.slices_resource_block[i].id = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].group_id = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].origin = psd_stream_get_int(context); // NOTE: Only present if Origin = 1 if(context->slices_resource.slices_resource_block[i].origin == 1) context->slices_resource.slices_resource_block[i].associated_layer_id = psd_stream_get_int(context); // Name: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].type = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].left = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].top = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].right = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].bottom = psd_stream_get_int(context); // URL: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Target: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Message: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); // Alt Tag: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].cell_text_is_html = psd_stream_get_char(context); // Cell text: Unicode string size = psd_stream_get_int(context) * 2; psd_stream_get_null(context, size); context->slices_resource.slices_resource_block[i].horizontal_alignment = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].veritcal_alignment = psd_stream_get_int(context); context->slices_resource.slices_resource_block[i].color = psd_argb_to_color(psd_stream_get_char(context), psd_stream_get_char(context), psd_stream_get_char(context), psd_stream_get_char(context)); } context->fill_slices_resource = psd_true; break; // (Photoshop 6.0) URL List case 1054: // 4 byte count of URLs context->url_list.number_of_urls = psd_stream_get_int(context); if (context->url_list.number_of_urls > 0) { context->url_list.items = (psd_url_list_item *)psd_malloc(sizeof(psd_url_list_item) * context->url_list.number_of_urls); if (context->url_list.items == NULL) return psd_status_malloc_failed; memset(context->url_list.items, 0, sizeof(psd_url_list_item) * context->url_list.number_of_urls); } for (i = 0; i < context->url_list.number_of_urls; i ++) { // followed by 4 byte long context->url_list.items[i].tag = psd_stream_get_int(context); // 4 byte ID context->url_list.items[i].ID = psd_stream_get_int(context); // Unicode string for each count. context->url_list.items[i].name_length = psd_stream_get_int(context); context->url_list.items[i].name = (psd_ushort *)psd_malloc(2 * context->url_list.items[i].name_length); if (context->url_list.items[i].name == NULL) return psd_status_malloc_failed; psd_stream_get(context, (psd_uchar *)context->url_list.items[i].name, 2 * context->url_list.items[i].name_length); } context->fill_url_list = psd_true; break; // (Photoshop 7.0) EXIF data 1 case 1058: // (Photoshop 7.0) EXIF data 3 // http://www.pima.net/standards/it10/PIMA15740/exif.htm case 1059: // avoid to get the exif data for twice psd_assert(context->fill_exif_data == psd_false); # ifdef PSD_INCLUDDE_LIBEXIF buffer = (psd_uchar *)psd_malloc(sizeofdata + sizeof(ExifHeader)); if (buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer + sizeof(ExifHeader), sizeofdata); memcpy(buffer, ExifHeader, sizeof(ExifHeader)); context->exif_data = (psd_uchar *)exif_data_new_from_data(buffer, sizeofdata + sizeof(ExifHeader)); psd_free(buffer); context->fill_exif_data = psd_true; # else // ifdef PSD_INCLUDDE_LIBEXIF context->exif_data = (psd_uchar *)psd_malloc(sizeofdata); if (context->exif_data == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->exif_data, sizeofdata); context->exif_data_length = sizeofdata; context->fill_exif_data = psd_true; # endif // ifdef PSD_INCLUDDE_LIBEXIF break; // (Photoshop 7.0) XMP metadata // File info as XML description // http://Partners.adobe.com/asn/developer/xmp/main.html case 1060: # ifdef PSD_INCLUDE_LIBXML buffer = (psd_uchar *)psd_malloc(sizeofdata); if (buffer == NULL) return psd_status_malloc_failed; psd_stream_get(context, buffer, sizeofdata); context->XMP_metadata = (psd_uchar *)xmlParseMemory(buffer, sizeofdata); psd_free(buffer); context->fill_XMP_metadata = psd_true; # else // ifdef PSD_INCLUDE_LIBXML context->XMP_metadata = (psd_uchar *)psd_malloc(sizeofdata); if (context->XMP_metadata == NULL) return psd_status_malloc_failed; psd_stream_get(context, context->XMP_metadata, sizeofdata); context->XMP_metadata_length = sizeofdata; context->fill_XMP_metadata = psd_true; # endif // ifdef PSD_INCLUDE_LIBXML break; // (Photoshop 7.0) Print scale case 1062: // 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). context->print_scale.style = psd_stream_get_short(context); psd_assert(context->print_scale.style >= psd_print_centered && context->print_scale.style <= psd_print_user_defined); // 4 bytes x location (floating point). context->print_scale.x_location = psd_stream_get_float(context); // 4 bytes y location (floating point). context->print_scale.y_location = psd_stream_get_float(context); // 4 bytes scale (floating point) context->print_scale.scale = psd_stream_get_float(context); context->fill_print_scale = psd_true; break; // (Photoshop CS) Pixel Aspect Ratio case 1064: // 4 bytes (version = 1) psd_assert(psd_stream_get_int(context) == 1); // 8 bytes double, x / y of a pixel context->pixel_aspect_ratio = psd_stream_get_double(context); break; // Print flags information case 10000: // 2 bytes version ( = 1) psd_assert(psd_stream_get_short(context) == 1); // 1 byte center crop marks context->print_flags_info.center_crop = psd_stream_get_bool(context); // 1 byte ( = 0) psd_assert(psd_stream_get_char(context) == 0); // 4 bytes bleed width value context->print_flags_info.value = psd_stream_get_int(context); // 2 bytes bleed width scale context->print_flags_info.scale = psd_stream_get_short(context); context->fill_print_flags_info = psd_true; break; #endif // ifdef PSD_GET_ALL_IMAGE_RESOURCE ///////////////////////////////////////////////////////////////////////////////////////////// default: #ifdef PSD_GET_PATH_RESOURCE // Photoshop stores its paths as resources of type 8BIM, with IDs in the range 2000 // through 2998. if(ID >= 2000 && ID <= 2998) { psd_get_path(context, sizeofdata); } // If the file contains a resource of type 8BIM with an ID of 2999, then this resource // contains a Pascal¨Cstyle string containing the name of the clipping path to use with this // image when saving it as an EPS file??? else if(ID == 2999) { // we don't find any files includes the name of the clipping path. psd_assert(0); } else #endif // ifdef PSD_GET_PATH_RESOURCE { psd_stream_get_null(context, sizeofdata); } break; } // Filler psd_stream_get_null(context, prev_stream_pos + sizeofdata - context->stream.current_pos); } } else { return psd_status_resource_signature_error; } } return psd_status_done; }