// Solid color sheet setting (Photoshop 6.0) psd_status psd_get_layer_solid_color(psd_context * context, psd_layer_record * layer) { psd_layer_solid_color * data; psd_int length, number_items; psd_uint key; layer->layer_info_type[layer->layer_info_count] = psd_layer_info_type_solid_color; layer->layer_type = psd_layer_type_solid_color; data = (psd_layer_solid_color *)psd_malloc(sizeof(psd_layer_solid_color)); if(data == NULL) return psd_status_malloc_failed; memset(data, 0, sizeof(psd_layer_solid_color)); layer->layer_info_data[layer->layer_info_count] = (psd_uint)data; layer->layer_info_count ++; // Version ( = 16 for Photoshop 6.0) if(psd_stream_get_int(context) != 16) return psd_status_solid_color_unsupport_version; // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); psd_assert(length == 0); data->id = psd_stream_get_int(context); // Number of items in descriptor number_items = psd_stream_get_int(context); // should be 1 psd_assert(number_items == 1); // The following is repeated for each item in descriptor // Key: 4 bytes ( length) followed either by string or (if length is zero) 4-byte key length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); psd_assert(key == 'Clr '); // Type: OSType key key = psd_stream_get_int(context); psd_assert(key == 'Objc'); data->fill_color = psd_stream_get_object_color(context); return psd_status_done; }
psd_status psd_get_layer_threshold(psd_context * context, psd_layer_record * layer) { psd_layer_threshold * data; layer->layer_info_type[layer->layer_info_count] = psd_layer_info_type_threshold; layer->layer_type = psd_layer_type_threshold; data = (psd_layer_threshold *)psd_malloc(sizeof(psd_layer_threshold)); if(data == NULL) return psd_status_malloc_failed; memset(data, 0, sizeof(psd_layer_threshold)); layer->layer_info_data[layer->layer_info_count] = (psd_uint)data; layer->layer_info_count ++; // (1...255) data->level = psd_stream_get_short(context); psd_assert(data->level >= 1 && data->level <= 255); // for padding ? psd_stream_get_short(context); layer->adjustment_valid = psd_true; return psd_status_done; }
psd_status psd_get_layer_vector_mask(psd_context * context, psd_layer_record * layer, psd_int size) { psd_uint tag; // Version ( = 3 for Photoshop 6.0) psd_assert(psd_stream_get_int(context) == 3); // Flags. bit 1 = invert, bit 2 = not link, bit 3 = disable tag = psd_stream_get_int(context); layer->vector_mask.invert = tag & 0x01; layer->vector_mask.not_link = (tag & (0x01 << 1)) > 0; layer->vector_mask.disable = (tag & (0x01 << 2)) > 0; // The rest of the data is path components, loop until end of the length layer->vector_mask.path = (psd_path *)psd_malloc(sizeof(psd_path)); if(layer->vector_mask.path == NULL) return psd_status_malloc_failed; psd_get_path_record(context, layer->vector_mask.path, size - 8); return psd_status_done; }
psd_static psd_status psd_main_loop(psd_context * context) { psd_status status = psd_status_done; while(status == psd_status_done) { if(context->stream.file_length <= 0) { status = psd_status_fread_error; break; } switch(context->state) { case PSD_FILE_HEADER: status = psd_get_file_header(context); if(status == psd_status_done) { if (context->load_tag == psd_load_tag_header) context->state = PSD_DONE; else context->state = PSD_COLOR_MODE_DATA; } else if(status == psd_status_unkown_error) status = psd_status_file_header_error; break; case PSD_COLOR_MODE_DATA: status = psd_get_color_mode_data(context); if(status == psd_status_done) context->state = PSD_IMAGE_RESOURCE; else if(status == psd_status_unkown_error) status = psd_status_color_mode_data_error; break; case PSD_IMAGE_RESOURCE: status = psd_get_image_resource(context); if(status == psd_status_done) context->state = PSD_LAYER_AND_MASK_INFORMATION; else if(status == psd_status_unkown_error) status = psd_status_image_resource_error; break; case PSD_LAYER_AND_MASK_INFORMATION: status = psd_get_layer_and_mask(context); if(status == psd_status_done) context->state = PSD_IMAGE_DATA; else if(status == psd_status_unkown_error) status = psd_status_layer_and_mask_error; break; case PSD_IMAGE_DATA: status = psd_get_image_data(context); if(status == psd_status_done) context->state = PSD_DONE; else if(status == psd_status_unkown_error) status = psd_status_image_data_error; break; case PSD_DONE: return psd_status_done; default: psd_assert(0); return psd_status_unkown_error; } } return status; }
// The file header contains the basic properties of the image // input: context, the context of psd file // output: status of done psd_status psd_get_file_header(psd_context * context) { psd_file_header_binary header; if(psd_stream_get(context, (psd_uchar *)&header, sizeof(psd_file_header_binary)) == sizeof(psd_file_header_binary)) { // Signature: always equal to '8BPS' if(memcmp(header.signature, "8BPS", 4) != 0) return psd_status_file_signature_error; // Version: always equal to 1 if(PSD_CHAR_TO_SHORT(header.version) != 1) return psd_status_file_version_error; // The height of the image in pixels context->height = PSD_CHAR_TO_INT(header.height_of_image); // Supported range is 1 to 30,000 psd_assert(context->height >= 1 && context->height <= 30000); // The width of the image in pixels context->width = PSD_CHAR_TO_INT(header.width_of_image); // Supported range is 1 to 30,000 psd_assert(context->width >= 1 && context->width <= 30000); // The number of channels in the image, including any alpha channels context->color_channels = context->channels = PSD_CHAR_TO_SHORT(header.number_of_channels); // Supported range is 1 to 56. psd_assert(context->channels >= 1 && context->channels <= 56); // Depth: the number of bits per channel context->depth = PSD_CHAR_TO_SHORT(header.depth); // Supported values are 1, 8, and 16. psd_assert(context->depth == 1 || context->depth == 8 || context->depth == 16); if(context->depth != 1 && context->depth != 8 && context->depth != 16) return psd_status_unsupport_color_depth; // The color mode of the file context->color_mode = (psd_color_mode)PSD_CHAR_TO_SHORT(header.color_mode); switch(context->color_mode) { case psd_color_mode_bitmap: case psd_color_mode_grayscale: case psd_color_mode_indexed: case psd_color_mode_rgb: case psd_color_mode_duotone: break; case psd_color_mode_cmyk: #ifndef PSD_SUPPORT_CMYK return psd_status_unknown_color_mode; #endif break; case psd_color_mode_lab: #ifndef PSD_SUPPORT_LAB return psd_status_unknown_color_mode; #endif break; case psd_color_mode_multichannel: #ifndef PSD_SUPPORT_MULTICHANNEL return psd_status_unknown_color_mode; #endif break; default: psd_assert(0); return psd_status_unknown_color_mode; } } else { return psd_status_fread_error; } return psd_status_done; }
psd_status psd_get_layer_drop_shadow2(psd_context * context, psd_layer_effects_drop_shadow * drop_shadow) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_drop_shadow_default(drop_shadow); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { case 0: // layer knocks out drop shadow if(strcmp(keychar, "layerConceals") == 0) { psd_assert(type == 'bool'); drop_shadow->knocks_out = psd_stream_get_bool(context); } else { psd_assert(0); } break; // effect enable case 'enab': psd_assert(type == 'bool'); drop_shadow->effect_enable = psd_stream_get_bool(context); break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // blend mode psd_assert(key == 'BlnM'); drop_shadow->blend_mode = psd_stream_get_object_blend_mode(context); break; // color or native color case 'Clr ': // Descriptor psd_assert(type == 'Objc'); drop_shadow->color = drop_shadow->native_color = psd_stream_get_object_color(context); break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) drop_shadow->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // use global light case 'uglg': psd_assert(type == 'bool'); drop_shadow->use_global_light = psd_stream_get_bool(context); break; // angle case 'lagl': psd_assert(type == 'UntF'); // angle: base degrees key = psd_stream_get_int(context); psd_assert(key == '#Ang'); drop_shadow->angle = (psd_int)psd_stream_get_double(context); break; // distance case 'Dstn': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); drop_shadow->distance = (psd_int)psd_stream_get_double(context); break; // spread case 'Ckmt': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); drop_shadow->spread = (psd_int)psd_stream_get_double(context); break; // size case 'blur': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); drop_shadow->size = (psd_int)psd_stream_get_double(context); break; // noise case 'Nose': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) drop_shadow->noise = (psd_int)psd_stream_get_double(context); break; // anti-aliased case 'AntA': psd_assert(type == 'bool'); drop_shadow->anti_aliased = psd_stream_get_bool(context); break; // contour case 'TrnS': psd_assert(type == 'Objc'); psd_stream_get_object_contour(drop_shadow->contour_lookup_table, context); break; default: psd_assert(0); break; } } return psd_status_done; }
psd_status psd_get_layer_color_overlay2(psd_context * context, psd_layer_effects_color_overlay * color_overlay) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_color_overlay_default(color_overlay); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); psd_assert(length == 0); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { // effect enable case 'enab': psd_assert(type == 'bool'); color_overlay->effect_enable = psd_stream_get_bool(context); break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // blend mode psd_assert(key == 'BlnM'); color_overlay->blend_mode = psd_stream_get_object_blend_mode(context); break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) color_overlay->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // color or native color case 'Clr ': // Descriptor psd_assert(type == 'Objc'); color_overlay->color = color_overlay->native_color = psd_stream_get_object_color(context); break; default: psd_assert(0); break; } } return psd_status_done; }
// Additional layer -- levels // Levels settings files are loaded and saved in the Levels dialog. psd_status psd_get_layer_levels(psd_context * context, psd_layer_record * layer, psd_int data_length) { psd_layer_levels * data; psd_int i, prev_stream_pos = context->stream.current_pos; psd_uint tag; layer->layer_info_type[layer->layer_info_count] = psd_layer_info_type_levels; layer->layer_type = psd_layer_type_levels; data = (psd_layer_levels *)psd_malloc(sizeof(psd_layer_levels)); if(data == NULL) return psd_status_malloc_failed; memset(data, 0, sizeof(psd_layer_levels)); layer->layer_info_data[layer->layer_info_count] = (psd_uint)data; layer->layer_info_count ++; // Version ( = 2) if(psd_stream_get_short(context) != 2) return psd_status_levels_unsupport_version; // 29 sets of level records, each level containing 5 psd_short integers for(i = 0; i < 29; i ++) { // Input floor (0...253) data->record[i].input_floor = psd_stream_get_short(context); // Input ceiling (2...255) data->record[i].input_ceiling = psd_stream_get_short(context); // Output floor (0...255). Matched to input floor. data->record[i].output_floor = psd_stream_get_short(context); // Output ceiling (0...255) data->record[i].output_ceiling = psd_stream_get_short(context); // Gamma. Short integer from 10...999 representing 0.1...9.99. Applied // to all image data. data->record[i].gamma = psd_stream_get_short(context) / 100.0f; // Sets 28 and 29 are reserved and should be set to zeros. if(i < 27) { psd_assert(data->record[i].input_floor >= 0 && data->record[i].input_floor <= 255); psd_assert(data->record[i].input_ceiling >= 2 && data->record[i].input_ceiling <= 255); psd_assert(data->record[i].output_floor >= 0 && data->record[i].output_floor <= 255); psd_assert(data->record[i].output_ceiling >= 0 && data->record[i].output_ceiling <= 255); psd_assert(data->record[i].gamma >= 0.1 && data->record[i].gamma <= 9.99); } } // Photoshop CS (8.0) Additional information // At the end of the Version 2 file is the following information if(context->stream.current_pos - prev_stream_pos < data_length - 4) { // = 'Lvls' for extra level information tag = psd_stream_get_int(context); if(tag != 'Lvls') return psd_status_extra_levels_key_error; // Version ( = 3) if(psd_stream_get_short(context) != 3) return psd_status_extra_levels_unsupport_version; // Count of total level record structures. Subtract the legacy number of // level record structures, 29, to determine how many are remaining in // the file for reading. data->extra_level_count = psd_stream_get_short(context) - 29; psd_assert(data->extra_level_count >= 0); data->extra_record = (psd_layer_level_record *)psd_malloc(data->extra_level_count * sizeof(psd_layer_level_record)); if(data->extra_record == NULL) return psd_status_malloc_failed; memset(data->extra_record, 0, data->extra_level_count * sizeof(psd_layer_level_record)); // Additianol level records according to count. for(i = 0; i < data->extra_level_count; i ++) { // Input floor (0...253) data->extra_record[i].input_floor = psd_stream_get_short(context); // Input ceiling (2...255) data->extra_record[i].input_ceiling = psd_stream_get_short(context); // Output floor (0...255). Matched to input floor. data->extra_record[i].output_floor = psd_stream_get_short(context); // Output ceiling (0...255) data->extra_record[i].output_ceiling = psd_stream_get_short(context); // Gamma. Short integer from 10...999 representing 0.1...9.99. Applied // to all image data. data->extra_record[i].gamma = psd_stream_get_short(context) / 100.0f; // Sets 28 and 29 are reserved and should be set to zeros. } } if(context->stream.current_pos - prev_stream_pos != data_length) return psd_status_levels_dismatch_data_length; layer->adjustment_valid = psd_true; return psd_status_done; }
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_get_layer_inner_glow2(psd_context * context, psd_layer_effects_inner_glow * inner_glow) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_inner_glow_default(inner_glow); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); psd_assert(length == 0); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { // effect enable case 'enab': psd_assert(type == 'bool'); inner_glow->effect_enable = psd_stream_get_bool(context); break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // blend mode psd_assert(key == 'BlnM'); inner_glow->blend_mode = psd_stream_get_object_blend_mode(context); break; // color or native color case 'Clr ': // Descriptor psd_assert(type == 'Objc'); inner_glow->color = inner_glow->native_color = psd_stream_get_object_color(context); inner_glow->fill_type = psd_fill_solid_color; break; // gradient color case 'Grad': // Descriptor psd_assert(type == 'Objc'); psd_stream_get_object_gradient_color(&inner_glow->gradient_color, context); inner_glow->fill_type = psd_fill_gradient; break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) inner_glow->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // technique case 'GlwT': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // Matte Technique psd_assert(key == 'BETE'); inner_glow->technique = psd_stream_get_object_technique(context); break; // choke case 'Ckmt': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); inner_glow->choke = (psd_int)psd_stream_get_double(context); break; // size case 'blur': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); inner_glow->size = (psd_int)psd_stream_get_double(context); break; // jitter case 'ShdN': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) inner_glow->jitter = (psd_int)psd_stream_get_double(context); break; // noise case 'Nose': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) inner_glow->noise = (psd_int)psd_stream_get_double(context); break; // anti-aliased case 'AntA': psd_assert(type == 'bool'); inner_glow->anti_aliased = psd_stream_get_bool(context); break; // source case 'glwS': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // inner glow source psd_assert(key == 'IGSr'); length = psd_stream_get_int(context); if(length == 0) key = psd_stream_get_int(context); else { key = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } switch(key) { case 'SrcC': inner_glow->source = psd_glow_center; break; case 'SrcE': inner_glow->source = psd_glow_edge; break; default: psd_assert(0); break; } break; // contour case 'TrnS': psd_assert(type == 'Objc'); psd_stream_get_object_contour(inner_glow->contour_lookup_table, context); break; // range case 'Inpr': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) inner_glow->range = (psd_int)psd_stream_get_double(context); break; default: psd_assert(0); psd_stream_get_object_null(type, context); break; } } return psd_status_done; }
// Type Tool Info (Photoshop 5.0 and 5.5 only) psd_status psd_get_layer_type_tool(psd_context * context, psd_layer_record * layer) { psd_layer_type_tool * data; psd_int i, j, length; layer->layer_info_type[layer->layer_info_count] = psd_layer_info_type_type_tool; data = (psd_layer_type_tool *)psd_malloc(sizeof(psd_layer_type_tool)); if(data == NULL) return psd_status_malloc_failed; memset(data, 0, sizeof(psd_layer_type_tool)); layer->layer_info_data[layer->layer_info_count] = (psd_uint)data; layer->layer_info_count ++; // Version ( = 1) psd_assert(psd_stream_get_short(context) == 1); // 6 * 8 double precision numbers for the transform information for(i = 0; i < 6; i ++) data->transform_info[i] = psd_stream_get_double(context); /***********************************************************************/ // Font information /***********************************************************************/ // Version ( = 6) psd_assert(psd_stream_get_short(context) == 6); // Count of faces data->faces_count = psd_stream_get_short(context); data->face = (psd_layer_type_face *)psd_malloc(data->faces_count * sizeof(psd_layer_type_face)); if(data->face == NULL) return psd_status_malloc_failed; memset(data->face, 0, data->faces_count * sizeof(psd_layer_type_face)); // The next 8 fields are repeated for each count specified above for(i = 0; i < data->faces_count; i ++) { // Mark value data->face[i].mark = psd_stream_get_short(context); // Font type data data->face[i].font_type = psd_stream_get_int(context); // Pascal string of font name length = psd_stream_get_char(context); psd_stream_get(context, data->face[i].font_name, length); // Pascal string of font family name length = psd_stream_get_char(context); psd_stream_get(context, data->face[i].font_family_name, length); // Pascal string of font style name length = psd_stream_get_char(context); psd_stream_get(context, data->face[i].font_style_name, length); // Script value data->face[i].script = psd_stream_get_short(context); // Number of design axes vector to follow data->face[i].number_axes_vector = psd_stream_get_int(context); data->face[i].vector = (psd_int *)psd_malloc(data->face[i].number_axes_vector * 4); if(data->face[i].vector == NULL) return psd_status_malloc_failed; // Design vector value for(j = 0; j < data->face[i].number_axes_vector; j ++) data->face[i].vector[j] = psd_stream_get_int(context); } /***********************************************************************/ // Style information /***********************************************************************/ // Count of styles data->styles_count = psd_stream_get_short(context); data->style = (psd_layer_type_style *)psd_malloc(data->styles_count * sizeof(psd_layer_type_style)); if(data->style == NULL) return psd_status_malloc_failed; memset(data->style, 0, data->styles_count * sizeof(psd_layer_type_style)); // The next 10 fields are repeated for each count specified above for(i = 0; i < data->styles_count; i ++) { // Mark value data->style[i].mark = psd_stream_get_short(context); // Face mark value data->style[i].face_mark = psd_stream_get_short(context); // Size value data->style[i].size = psd_stream_get_int(context); // Tracking value data->style[i].tracking = psd_stream_get_int(context); // Kerning value data->style[i].kerning = psd_stream_get_int(context); // Leading value data->style[i].leading = psd_stream_get_int(context); // Base shift value data->style[i].base_shift = psd_stream_get_int(context); // Auto kern on/off data->style[i].auto_kern = psd_stream_get_bool(context); // Only present in version <= 5 psd_stream_get_char(context); // Rotate up/down data->style[i].rotate = psd_stream_get_bool(context); } /***********************************************************************/ // Text information /***********************************************************************/ // Type value data->type = psd_stream_get_short(context); // Scaling factor value data->scaling_factor = psd_stream_get_int(context); // Sharacter count value data->sharacter_count = psd_stream_get_int(context); // Horizontal placement data->horz_place = psd_stream_get_int(context); // Vertical placement data->vert_place = psd_stream_get_int(context); // Select start value data->select_start = psd_stream_get_int(context); // Select end value data->select_end = psd_stream_get_int(context); // Line count data->lines_count = psd_stream_get_short(context); data->line = (psd_layer_type_line *)psd_malloc(data->lines_count * sizeof(psd_layer_type_line)); if(data->line == NULL) return psd_status_malloc_failed; memset(data->line, 0, data->lines_count * sizeof(psd_layer_type_line)); // The next 5 fields are repeated for each item in line count. for(i = 0; i < data->lines_count; i ++) { // Character count value data->line[i].char_count = psd_stream_get_int(context); // Orientation value data->line[i].orientation = psd_stream_get_short(context); // Alignment value data->line[i].alignment = psd_stream_get_short(context); // Actual character as a double byte character data->line[i].actual_char = psd_stream_get_short(context); // Style value data->line[i].style = psd_stream_get_short(context); } /***********************************************************************/ // Color information /***********************************************************************/ // Color space value data->color = psd_stream_get_space_color(context); // Anti alias on/off data->anti_alias = psd_stream_get_bool(context); return psd_status_done; }
static psd_status psd_get_path_record(psd_context * context, psd_path * path, psd_int length) { psd_int i, records, malloc_subpath; psd_short record_type; psd_subpath * subpaths = NULL, * cur_subpath; psd_bezier_point * cur_bezier_point = NULL; memset(path, 0, sizeof(psd_path)); malloc_subpath = PSD_MIN_SUBPATH_COUNT; subpaths = (psd_subpath *)psd_malloc(sizeof(psd_subpath) * malloc_subpath); if (subpaths == NULL) return psd_status_malloc_failed; // These resource blocks consist of a series of 26-byte path point records, so the resource // length should always be a multiple of 26. records = length / 26; for (i = 0; i < records; i ++) { record_type = psd_stream_get_short(context); switch(record_type) { case 0: // Closed subpath length record case 3: // Open subpath length record if (path->number_of_subpaths >= malloc_subpath) { malloc_subpath *= 2; subpaths = (psd_subpath *)psd_realloc(subpaths, sizeof(psd_subpath) * malloc_subpath); if (subpaths == NULL) return psd_status_malloc_failed; } cur_subpath = &subpaths[path->number_of_subpaths]; path->number_of_subpaths ++; // contain the number of Bezier knot records in bytes 2 and 3 cur_subpath->number_of_points = psd_stream_get_short(context); cur_subpath->closed = (record_type == 0 ? psd_true : psd_false); cur_subpath->bezier_points = (psd_bezier_point *)psd_malloc(sizeof(psd_bezier_point) * cur_subpath->number_of_points); if (cur_subpath->bezier_points == NULL) return psd_status_malloc_failed; memset(cur_subpath->bezier_points, 0, sizeof(psd_bezier_point) * cur_subpath->number_of_points); cur_bezier_point = cur_subpath->bezier_points; // remains 22 byets psd_stream_get_null(context, 22); break; case 1: // Closed subpath Bezier knot, linked case 2: // Closed subpath Bezier knot, unlinked case 4: // Open subpath Bezier knot, linked case 5: // Open subpath Bezier knot, unlinked if (record_type == 1 || record_type == 4) cur_bezier_point->linked = psd_true; else cur_bezier_point->linked = psd_false; // the control point for the Bezier segment preceding the knot cur_bezier_point->preceding_control_vertical = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); cur_bezier_point->preceding_control_horizontal = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); // the anchor point for the knot cur_bezier_point->anchor_point_vertical = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); cur_bezier_point->anchor_point_horizontal = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); // the control point for the Bezier segment leaving the knot cur_bezier_point->leaving_control_vertical = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); cur_bezier_point->leaving_control_horizontal = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); // get the next cur_bezier_point ++; break; case 6: // Path fill rule record // The remaining 24 bytes of the first record are zeroes. psd_stream_get_null(context, 24); break; case 7: // Clipboard record // contain four fixed-point numbers for the bounding rectangle (top, left, bottom, right) path->clipboard_top = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); path->clipboard_left = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); path->clipboard_bottom = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); path->clipboard_right = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); // a single fixed-point number indicating the resolution path->resolution = psd_fixed_8_24_tofloat(psd_stream_get_int(context)); // remains 4 byets psd_stream_get_null(context, 4); break; case 8: // Initial fill rule record // contain one two byte record. A value of 1 means that // the fill starts with all pixels. path->initial_fill = (psd_bool)psd_stream_get_short(context); // remains 22 byets psd_stream_get_null(context, 22); break; default: psd_stream_get_null(context, 24); psd_assert(0); break; } } path->subpaths = subpaths; return psd_status_done; }
psd_status psd_get_layer_pattern_overlay2(psd_context * context, psd_layer_effects_pattern_overlay * pattern_overlay) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_pattern_overlay_default(pattern_overlay); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { case 0: if(strcmp(keychar, "phase") == 0) { psd_assert(type == 'Objc'); psd_stream_get_object_point(&pattern_overlay->horz_phase, &pattern_overlay->vert_phase, context); // problem, why ??? pattern_overlay->horz_phase = 0; pattern_overlay->vert_phase = 0; } else { psd_assert(0); psd_stream_get_object_null(type, context); } break; // effect enable case 'enab': psd_assert(type == 'bool'); pattern_overlay->effect_enable = psd_stream_get_bool(context); break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // Gradient Type psd_assert(key == 'BlnM'); pattern_overlay->blend_mode = psd_stream_get_object_blend_mode(context); break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) pattern_overlay->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // pattern case 'Ptrn': psd_assert(type == 'Objc'); psd_stream_get_object_pattern_info(&pattern_overlay->pattern_info, context); break; // scale case 'Scl ': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) pattern_overlay->scale = (psd_int)psd_stream_get_double(context); break; // link with layer case 'Algn': psd_assert(type == 'bool'); pattern_overlay->link_with_layer = psd_stream_get_bool(context); break; default: psd_assert(0); psd_stream_get_object_null(type, context); break; } } return psd_status_done; }
// shows the high-level organization of the layer information. psd_static psd_status psd_get_layer_info(psd_context * context) { psd_int length, extra_length, i, j, size; psd_int prev_stream_pos, prev_layer_stream_pos, extra_stream_pos; psd_bool skip_first_alpha = psd_false; psd_layer_record * layer, * group_layer; psd_uchar flags; psd_uint tag; psd_status status = psd_status_done; // Length of the layers info section. (**PSB** length is 8 bytes.) length = psd_stream_get_int(context); // rounded up to a multiple of 2 if(length & 0x01) length ++; if(length <= 0) return psd_status_done; prev_layer_stream_pos = context->stream.current_pos; // Layer count. context->layer_count = psd_stream_get_short(context); // If it is a negative number, its absolute value is the number of // layers and the first alpha channel contains the transparency data for the // merged result. if(context->layer_count < 0) { skip_first_alpha = psd_true; context->layer_count = -context->layer_count; } psd_assert(context->layer_count > 0); context->layer_records = (psd_layer_record *)psd_malloc(context->layer_count * sizeof(psd_layer_record)); if(context->layer_records == NULL) return psd_status_malloc_failed; memset(context->layer_records, 0, context->layer_count * sizeof(psd_layer_record)); for(i = 0, layer = context->layer_records; i < context->layer_count; i ++, layer ++) { // INFORMATION ABOUT EACH LAYER // value as default layer->layer_type = psd_layer_type_normal; layer->blend_mode = psd_blend_mode_normal; layer->opacity = 255; layer->fill_opacity = 255; layer->blend_clipped = psd_true; layer->blend_interior = psd_false; layer->knockout = 0; layer->transparency_shapes_layer = psd_true; layer->layer_mask_hides_effects = psd_false; layer->vector_mask_hides_effects = psd_false; layer->divider_blend_mode = psd_blend_mode_pass_through; layer->layer_mask_info.default_color = 255; layer->layer_mask_info.disabled = psd_true; // Rectangle containing the contents of the layer. Specified as top, left, // bottom, right coordinates layer->top = psd_stream_get_int(context); layer->left = psd_stream_get_int(context); layer->bottom = psd_stream_get_int(context); layer->right = psd_stream_get_int(context); layer->width = layer->right - layer->left; layer->height = layer->bottom - layer->top; // the size of layer size is 0. //psd_assert(layer->width > 0 && layer->height > 0); // Number of channels in the layer layer->number_of_channels = psd_stream_get_short(context); psd_assert(layer->number_of_channels > 0); layer->channel_info = (psd_channel_info *)psd_malloc(layer->number_of_channels * sizeof(psd_channel_info)); if(layer->channel_info == NULL) { psd_layer_free(layer); return psd_status_malloc_failed; } // Channel information. Six bytes per channel, consisting of: // 2 bytes for Channel ID: 0 = red, 1 = green, etc.; // ¨C1 = transparency mask; ¨C2 = user supplied layer mask // 4 bytes for length of corresponding channel data. (**PSB** 8 bytes for // length of corresponding channel data.) for(j = 0; j < layer->number_of_channels; j ++) { layer->channel_info[j].channel_id = psd_stream_get_short(context); layer->channel_info[j].data_length = psd_stream_get_int(context); layer->channel_info[j].restricted = psd_false; } // Blend mode signature: '8BIM' tag = psd_stream_get_int(context); if(tag != '8BIM') { psd_layer_free(layer); return psd_status_blend_mode_signature_error; } // Blend mode key layer->blend_mode = psd_stream_get_blend_mode(context); // Opacity. 0 = transparent ... 255 = opaque layer->opacity = psd_stream_get_char(context); // Clipping: 0 = base, 1 = non¨Cbase layer->clipping = psd_stream_get_bool(context); // Flags flags = psd_stream_get_char(context); // bit 0 = transparency protected layer->transparency_protected = flags & 0x01; // bit 1 = visible layer->visible = (flags & (0x01 << 1)) > 0; layer->visible = psd_true - layer->visible; // bit 2 = obsolete layer->obsolete = (flags & (0x01 << 2)) > 0; // bit 3 = 1 for Photoshop 5.0 and later, tells if bit 4 has useful information if((flags & (0x01 << 3)) > 0) { // bit 4 = pixel data irrelevant to appearance of document layer->pixel_data_irrelevant = (flags & (0x01 << 4)) > 0; } // Filler (zero) flags = psd_stream_get_char(context); psd_assert(flags == 0); // Length of the extra data field ( = the total length of the next five fields). extra_length = psd_stream_get_int(context); extra_stream_pos = context->stream.current_pos; psd_assert(extra_length > 0); // LAYER MASK / ADJUSTMENT LAYER DATA // Size of the data: 36, 20, or 0. If zero, the following fields are not present size = psd_stream_get_int(context); psd_assert(size == 36 || size == 20 || size == 0); if(size > 0) { // Rectangle enclosing layer mask: Top, left, bottom, right layer->layer_mask_info.top = psd_stream_get_int(context); layer->layer_mask_info.left = psd_stream_get_int(context); layer->layer_mask_info.bottom = psd_stream_get_int(context); layer->layer_mask_info.right = psd_stream_get_int(context); layer->layer_mask_info.width = layer->layer_mask_info.right - layer->layer_mask_info.left; layer->layer_mask_info.height = layer->layer_mask_info.bottom - layer->layer_mask_info.top; // Default color. 0 or 255 layer->layer_mask_info.default_color = psd_stream_get_char(context); psd_assert(layer->layer_mask_info.default_color == 0 || layer->layer_mask_info.default_color == 255); // Flags flags = psd_stream_get_char(context); // bit 0 = position relative to layer layer->layer_mask_info.relative = flags & 0x01; // bit 1 = layer mask disabled layer->layer_mask_info.disabled = (flags & (0x01 << 1)) > 0; // bit 2 = invert layer mask when blending layer->layer_mask_info.invert = (flags & (0x01 << 2)) > 0; if(size == 20) { // Padding. Only present if size = 20. Otherwise the following is present psd_stream_get_short(context); } else { // Real Flags. Same as Flags information above. flags = psd_stream_get_char(context); // bit 0 = position relative to layer layer->layer_mask_info.relative = flags & 0x01; // bit 1 = layer mask disabled layer->layer_mask_info.disabled = (flags & (0x01 << 1)) > 0; // bit 2 = invert layer mask when blending layer->layer_mask_info.invert = (flags & (0x01 << 2)) > 0; // Real user mask background. 0 or 255. Same as Flags information above. layer->layer_mask_info.default_color = psd_stream_get_char(context); psd_assert(layer->layer_mask_info.default_color == 0 || layer->layer_mask_info.default_color == 255); // Rectangle enclosing layer mask: Top, left, bottom, right. layer->layer_mask_info.top = psd_stream_get_int(context); layer->layer_mask_info.left = psd_stream_get_int(context); layer->layer_mask_info.bottom = psd_stream_get_int(context); layer->layer_mask_info.right = psd_stream_get_int(context); layer->layer_mask_info.width = layer->layer_mask_info.right - layer->layer_mask_info.left; layer->layer_mask_info.height = layer->layer_mask_info.bottom - layer->layer_mask_info.top; } } // LAYER BLENDING RANGES DATA // Length of layer blending ranges data size = psd_stream_get_int(context); // Composite gray blend source. Contains 2 black values followed by 2 // white values. Present but irrelevant for Lab & Grayscale. layer->layer_blending_ranges.gray_black_src = psd_stream_get_short(context); layer->layer_blending_ranges.gray_white_src = psd_stream_get_short(context); // Composite gray blend destination range layer->layer_blending_ranges.gray_black_dst = psd_stream_get_short(context); layer->layer_blending_ranges.gray_white_dst = psd_stream_get_short(context); layer->layer_blending_ranges.number_of_blending_channels = (size - 8) / 8; if (layer->layer_blending_ranges.number_of_blending_channels <= 0) { psd_layer_free(layer); return psd_status_invalid_blending_channels; } layer->layer_blending_ranges.channel_black_src = (psd_ushort *)psd_malloc(layer->layer_blending_ranges.number_of_blending_channels * 2); layer->layer_blending_ranges.channel_white_src = (psd_ushort *)psd_malloc(layer->layer_blending_ranges.number_of_blending_channels * 2); layer->layer_blending_ranges.channel_black_dst = (psd_ushort *)psd_malloc(layer->layer_blending_ranges.number_of_blending_channels * 2); layer->layer_blending_ranges.channel_white_dst = (psd_ushort *)psd_malloc(layer->layer_blending_ranges.number_of_blending_channels * 2); if(layer->layer_blending_ranges.channel_black_src == NULL || layer->layer_blending_ranges.channel_white_src == NULL || layer->layer_blending_ranges.channel_black_dst == NULL || layer->layer_blending_ranges.channel_white_dst == NULL) { psd_layer_free(layer); return psd_status_malloc_failed; } for(j = 0; j < layer->layer_blending_ranges.number_of_blending_channels; j ++) { // channel source range layer->layer_blending_ranges.channel_black_src[j] = psd_stream_get_short(context); layer->layer_blending_ranges.channel_white_src[j] = psd_stream_get_short(context); // channel destination range layer->layer_blending_ranges.channel_black_dst[j] = psd_stream_get_short(context); layer->layer_blending_ranges.channel_white_dst[j] = psd_stream_get_short(context); } // Layer name: Pascal string, padded to a multiple of 4 bytes. size = psd_stream_get_char(context); size = ((size + 1 + 3) & ~0x03) - 1; psd_stream_get(context, layer->layer_name, size); while(context->stream.current_pos - extra_stream_pos < extra_length) { // ADDITIONAL LAYER INFORMATION // Signature tag = psd_stream_get_int(context); if(tag != '8BIM') return psd_status_layer_information_signature_error; // Key: a 4-character code tag = psd_stream_get_int(context); // Length data below, rounded up to an even byte count. // (**PSB**, the following keys have a length count of 8 bytes: LMsk, Lr16, // Layr, Mt16, Mtrn, Alph. size = psd_stream_get_int(context); size = (size + 1) & ~0x01; prev_stream_pos = context->stream.current_pos; // Adjustment layer // Adjustment layers can have one of the following keys status = psd_status_done; switch(tag) { case 'levl': status = psd_get_layer_levels(context, layer, size); break; case 'curv': status = psd_get_layer_curves(context, layer, size); break; case 'brit': status = psd_get_layer_brightness_contrast(context, layer); break; case 'blnc': status = psd_get_layer_color_balance(context, layer); break; //case 'hue ': // Old Hue/saturation, Photoshop 4.0 //break; case 'hue2': status = psd_get_layer_hue_saturation(context, layer); break; case 'selc': status = psd_get_layer_selective_color(context, layer); break; case 'thrs': status = psd_get_layer_threshold(context, layer); break; case 'nvrt': status = psd_get_layer_invert(context, layer); break; case 'post': status = psd_get_layer_posterize(context, layer); break; case 'mixr': status = psd_get_layer_channel_mixer(context, layer); break; case 'grdm': status = psd_get_layer_gradient_map(context, layer); break; case 'phfl': status = psd_get_layer_photo_filter(context, layer); break; case 'lrFX': status = psd_get_layer_effects(context, layer); break; case 'lfx2': status = psd_get_layer_effects2(context,layer); break; case 'tySh': status = psd_get_layer_type_tool(context, layer); break; //case 'TySh': // Type tool object setting (Photoshop 6.0) //break; case 'SoCo': status = psd_get_layer_solid_color(context, layer); break; case 'GdFl': status = psd_get_layer_gradient_fill(context, layer); break; case 'PtFl': status = psd_get_layer_pattern_fill(context, layer); break; case 'luni': status = psd_get_layer_unicode_name(context, layer); break; case 'lnsr': status = psd_get_layer_name_id(context, layer); break; case 'lyid': status = psd_get_layer_id(context, layer); break; case 'clbl': status = psd_get_layer_blend_clipped(context, layer); break; case 'infx': status = psd_get_layer_blend_interior(context, layer); break; case 'knko': status = psd_get_layer_knockout(context, layer); break; case 'lspf': status = psd_get_layer_protected(context, layer); break; case 'lclr': status = psd_get_layer_sheet_color(context, layer); break; case 'fxrp': status = psd_get_layer_reference_point(context, layer); break; case 'lyvr': status = psd_get_layer_version(context, layer); break; case 'tsly': status = psd_get_layer_transparency_shapes_layer(context, layer); break; case 'lmgm': status = psd_get_layer_layer_mask_hides_effects(context, layer); break; case 'vmgm': status = psd_get_layer_vector_mask_hides_effects(context, layer); break; case 'iOpa': status = psd_get_layer_fill_opacity(context, layer); break; case 'lsct': status = psd_get_layer_section_divider(context, layer, size); break; case 'brst': status = psd_get_layer_channel_blending_restrictions(context, layer, size); break; #ifdef PSD_GET_PATH_RESOURCE case 'vmsk': status = psd_get_layer_vector_mask(context, layer, size); break; #endif default: psd_stream_get_null(context, size); break; } if(status != psd_status_done) { psd_layer_free(layer); return status; } // Filler psd_stream_get_null(context, prev_stream_pos + size - context->stream.current_pos); psd_assert(layer->layer_info_count < psd_layer_info_type_count); } psd_assert(context->stream.current_pos - extra_stream_pos == extra_length); } for(i = 0, layer = context->layer_records; i < context->layer_count; i ++, layer ++) { // Channel image data. Contains one or more image data records for each layer. status = psd_get_layer_channel_image_data(context, layer); if(status != psd_status_done) { psd_layer_free(layer); return status; } } // Filler: zeros psd_stream_get_null(context, prev_layer_stream_pos + length - context->stream.current_pos); // group layer for(i = context->layer_count - 1, group_layer = NULL; i >= 0; i --) { layer = &context->layer_records[i]; switch(layer->layer_type) { case psd_layer_type_normal: layer->group_layer = group_layer; break; case psd_layer_type_folder: group_layer = layer; break; case psd_layer_type_hidden: group_layer = NULL; break; } } return psd_status_done; }
psd_status psd_layer_effects_blend_gradient_overlay(psd_context * context, psd_layer_record * layer, psd_layer_effects * data) { psd_layer_effects_gradient_overlay * gradient_overlay = &data->gradient_overlay; psd_bitmap src_bmp, dst_bmp; psd_layer_mask_info layer_mask_info; psd_int 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; data->left[psd_layer_effects_type_gradient_overlay] = 0; data->top[psd_layer_effects_type_gradient_overlay] = 0; data->right[psd_layer_effects_type_gradient_overlay] = layer->width; data->bottom[psd_layer_effects_type_gradient_overlay] = layer->height; data->blend_mode[psd_layer_effects_type_gradient_overlay] = gradient_overlay->blend_mode; data->opacity[psd_layer_effects_type_gradient_overlay] = gradient_overlay->opacity; data->width[psd_layer_effects_type_gradient_overlay] = layer->width; data->height[psd_layer_effects_type_gradient_overlay] = layer->height; if(data->image_data[psd_layer_effects_type_gradient_overlay] == NULL) { data->image_data[psd_layer_effects_type_gradient_overlay] = (psd_argb_color *)psd_malloc(layer->width * layer->height * 4); if(data->image_data[psd_layer_effects_type_gradient_overlay] == NULL) return psd_status_malloc_failed; } src_bmp.width = layer->width; src_bmp.height = layer->height; src_bmp.image_data = layer->image_data; dst_bmp.width = layer->width; dst_bmp.height = layer->height; dst_bmp.image_data = data->image_data[psd_layer_effects_type_gradient_overlay]; if(gradient_overlay->align_width_layer == psd_false) { center_x = context->width / 2 + gradient_overlay->horz_offset * context->width / 100; center_y = context->height / 2 + gradient_overlay->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 = gradient_overlay->angle; width = (context->width * gradient_overlay->scale + 100) / 200; height = (context->height * gradient_overlay->scale + 100) / 200; } else { center_x = layer->width / 2 + gradient_overlay->horz_offset * layer->width / 100; center_y = layer->height / 2 + gradient_overlay->vert_offset * layer->height / 100; corner_angle = (psd_int)(atan((psd_float)layer->height / layer->width) * 180 / PSD_PI + 0.5); angle = gradient_overlay->angle; width = (layer->width * gradient_overlay->scale + 100) / 200; height = (layer->height * gradient_overlay->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(gradient_overlay->style) { case psd_gradient_style_linear: psd_gradient_fill_linear(&dst_bmp, &gradient_overlay->gradient_color, gradient_overlay->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(&dst_bmp, &gradient_overlay->gradient_color, gradient_overlay->reverse, center_x, center_y, radius_corner); break; case psd_gradient_style_angle: psd_gradient_fill_angle(&dst_bmp, &gradient_overlay->gradient_color, gradient_overlay->reverse, center_x, center_y, gradient_overlay->angle); break; case psd_gradient_style_reflected: psd_gradient_fill_reflected(&dst_bmp, &gradient_overlay->gradient_color, gradient_overlay->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(&dst_bmp, &gradient_overlay->gradient_color, gradient_overlay->reverse, center_x, center_y, radius_corner, gradient_overlay->angle); break; default: psd_assert(0); break; } psd_bitmap_mix_alpha_channel(&dst_bmp, &src_bmp); 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; layer_mask_info.top -= layer->top; layer_mask_info.right -= layer->left; layer_mask_info.bottom -= layer->top; psd_bitmap_blend_mask(&dst_bmp, &layer_mask_info); } data->valid[psd_layer_effects_type_gradient_overlay] = psd_false; return psd_status_done; }
psd_status psd_get_layer_stroke2(psd_context * context, psd_layer_effects_stroke * stroke) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_stroke_default(stroke); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { case 0: if(strcmp(keychar, "phase") == 0) { psd_assert(type == 'Objc'); psd_stream_get_object_point(&stroke->pattern_horz_phase, &stroke->pattern_vert_phase, context); stroke->pattern_horz_phase = 0; stroke->pattern_vert_phase = 0; } else { psd_assert(0); psd_stream_get_object_null(type, context); } break; // effect enable case 'enab': psd_assert(type == 'bool'); stroke->effect_enable = psd_stream_get_bool(context); break; // position case 'Styl': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // fill style psd_assert(key == 'FStl'); length = psd_stream_get_int(context); if(length == 0) key = psd_stream_get_int(context); else { key = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } switch(key) { case 'OutF': stroke->position = psd_stroke_outside; break; case 'InsF': stroke->position = psd_stroke_inside; break; case 'CtrF': stroke->position = psd_stroke_center; break; default: psd_assert(0); break; } break; // fill type case 'PntT': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // fill style psd_assert(key == 'FrFl'); length = psd_stream_get_int(context); if(length == 0) key = psd_stream_get_int(context); else { key = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } switch(key) { case 'SClr': stroke->fill_type = psd_fill_solid_color; break; case 'GrFl': stroke->fill_type = psd_fill_gradient; break; case 'Ptrn': stroke->fill_type = psd_fill_pattern; break; default: psd_assert(0); break; } break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // blend mode psd_assert(key == 'BlnM'); stroke->blend_mode = psd_stream_get_object_blend_mode(context); break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) stroke->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // size case 'Sz ': psd_assert(type == 'UntF'); // pixels: tagged unit value key = psd_stream_get_int(context); psd_assert(key == '#Pxl'); stroke->size = (psd_int)psd_stream_get_double(context); break; // color case 'Clr ': // Descriptor psd_assert(type == 'Objc'); stroke->fill_color = psd_stream_get_object_color(context); break; // gradient color case 'Grad': // Descriptor psd_assert(type == 'Objc'); psd_stream_get_object_gradient_color(&stroke->gradient_color, context); break; case 'Angl': // angle // Unit psd_float psd_assert(type == 'UntF'); // '#Ang' = angle: base degrees key = psd_stream_get_int(context); psd_assert(key == '#Ang'); // Actual value (double) stroke->gradient_angle = (psd_int)psd_stream_get_double(context); break; case 'Type': // gradient style // Enumerated psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // Gradient Type psd_assert(key == 'GrdT'); stroke->gradient_style = psd_stream_get_object_gradient_style(context); break; case 'Rvrs': // reverse // boolean psd_assert(type == 'bool'); stroke->gradient_reverse = psd_stream_get_bool(context); break; case 'Scl ': // scale // Unit psd_float psd_assert(type == 'UntF'); // '#Prc' = percent: key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) if(stroke->fill_type == psd_fill_gradient) stroke->gradient_scale = (psd_int)psd_stream_get_double(context); else if(stroke->fill_type == psd_fill_pattern) stroke->pattern_scale = (psd_int)psd_stream_get_double(context); break; case 'Algn': // align with layer // boolean psd_assert(type == 'bool'); stroke->gradient_align = psd_stream_get_bool(context); break; // offset, not documented case 'Ofst': psd_assert(type == 'Objc'); psd_stream_get_object_point(&stroke->gradient_horz_offset, &stroke->gradient_vert_offset, context); break; // pattern case 'Ptrn': psd_assert(type == 'Objc'); psd_stream_get_object_pattern_info(&stroke->pattern_info, context); break; // link with layer case 'Lnkd': psd_assert(type == 'bool'); stroke->pattern_link = psd_stream_get_bool(context); break; default: psd_assert(0); psd_stream_get_object_null(type, context); break; } } return psd_status_done; }
psd_status psd_get_layer_gradient_overlay2(psd_context * context, psd_layer_effects_gradient_overlay * gradient_overlay) { psd_int length, number_items; psd_uint rootkey, type, key; psd_uchar keychar[256]; psd_set_layer_gradient_overlay_default(gradient_overlay); // Unicode string: name from classID length = psd_stream_get_int(context) * 2; psd_stream_get_null(context, length); // classID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte classID length = psd_stream_get_int(context); if(length == 0) psd_stream_get_int(context); else psd_stream_get_null(context, length); // Number of items in descriptor number_items = psd_stream_get_int(context); while(number_items --) { length = psd_stream_get_int(context); psd_assert(length == 0); if(length == 0) rootkey = psd_stream_get_int(context); else { rootkey = 0; psd_stream_get(context, keychar, length); keychar[length] = 0; } // Type: OSType key type = psd_stream_get_int(context); switch(rootkey) { // effect enable case 'enab': psd_assert(type == 'bool'); gradient_overlay->effect_enable = psd_stream_get_bool(context); break; // blend mode case 'Md ': psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // blend mode psd_assert(key == 'BlnM'); gradient_overlay->blend_mode = psd_stream_get_object_blend_mode(context); break; // opacity case 'Opct': psd_assert(type == 'UntF'); // percent key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) gradient_overlay->opacity = (psd_int)(psd_stream_get_double(context) * 2.55 + 0.5); break; // gradient color case 'Grad': // Descriptor psd_assert(type == 'Objc'); psd_stream_get_object_gradient_color(&gradient_overlay->gradient_color, context); break; case 'Angl': // angle // Unit psd_float psd_assert(type == 'UntF'); // '#Ang' = angle: base degrees key = psd_stream_get_int(context); psd_assert(key == '#Ang'); // Actual value (double) gradient_overlay->angle = (psd_int)psd_stream_get_double(context); break; case 'Type': // gradient style // Enumerated psd_assert(type == 'enum'); // TypeID: 4 bytes (length), followed either by string or (if length is zero) 4- // byte typeID length = psd_stream_get_int(context); psd_assert(length == 0); key = psd_stream_get_int(context); // Gradient Type psd_assert(key == 'GrdT'); gradient_overlay->style = psd_stream_get_object_gradient_style(context); break; case 'Rvrs': // reverse // boolean psd_assert(type == 'bool'); gradient_overlay->reverse = psd_stream_get_bool(context); break; case 'Algn': // align with layer // boolean psd_assert(type == 'bool'); gradient_overlay->align_width_layer = psd_stream_get_bool(context); break; case 'Scl ': // scale // Unit psd_float psd_assert(type == 'UntF'); // '#Prc' = percent: key = psd_stream_get_int(context); psd_assert(key == '#Prc'); // Actual value (double) gradient_overlay->scale = (psd_int)psd_stream_get_double(context); break; // offset, not documented case 'Ofst': psd_assert(type == 'Objc'); psd_stream_get_object_point(&gradient_overlay->horz_offset, &gradient_overlay->vert_offset, context); break; default: psd_assert(0); psd_stream_get_object_null(type, context); break; } } 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; }