/* * Initiates the gif decode */ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount, int* rowsDecoded) { Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts); if (kSuccess != result) { return result; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { return gif_error("Scaling not supported.\n", kInvalidScale); } // Initialize the swizzler if (fFrameIsSubset) { // Fill the background SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType()), opts.fZeroInitialized); } // Iterate over rows of the input for (int y = fFrameRect.top(); y < fFrameRect.bottom(); y++) { if (!this->readRow()) { *rowsDecoded = y; return gif_error("Could not decode line.\n", kIncompleteInput); } void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y)); fSwizzler->swizzle(dstRow, fSrcBuffer.get()); } return kSuccess; }
/* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. * * @param codecOut * If it returned true, and codecOut was not nullptr, * codecOut will be set to a new SkGifCodec. * * @param gifOut * If it returned true, and codecOut was nullptr, * gifOut must be non-nullptr and gifOut will be set to a new * GifFileType pointer. * * @param stream * Deleted on failure. * codecOut will take ownership of it in the case where we created a codec. * Ownership is unchanged when we returned a gifOut. * */ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { SkAutoTDelete<SkStream> streamDeleter(stream); // Read gif header, logical screen descriptor, and global color table SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); if (nullptr == gif) { gif_error("DGifOpen failed.\n"); return false; } // Read through gif extensions to get to the image data. Set the // transparent index based on the extension data. uint32_t transIndex; SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); if (kSuccess != result){ return false; } // Read the image descriptor if (GIF_ERROR == DGifGetImageDesc(gif)) { return false; } // If reading the image descriptor is successful, the image count will be // incremented. SkASSERT(gif->ImageCount >= 1); if (nullptr != codecOut) { SkISize size; SkIRect frameRect; if (!GetDimensions(gif, &size, &frameRect)) { gif_error("Invalid gif size.\n"); return false; } bool frameIsSubset = (size != frameRect.size()); // Determine the encoded alpha type. The transIndex might be valid if it less // than 256. We are not certain that the index is valid until we process the color // table, since some gifs have color tables with less than 256 colors. If // there might be a valid transparent index, we must indicate that the image has // alpha. // In the case where we must support alpha, we indicate kBinary, since every // pixel will either be fully opaque or fully transparent. SkEncodedInfo::Alpha alpha = (transIndex < 256) ? SkEncodedInfo::kBinary_Alpha : SkEncodedInfo::kOpaque_Alpha; // Return the codec // Use kPalette since Gifs are encoded with a color table. // Use 8-bits per component, since this is the output we get from giflib. // FIXME: Gifs can actually be encoded with 4-bits per pixel. Can we support this? SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8); *codecOut = new SkGifCodec(size.width(), size.height(), info, streamDeleter.release(), gif.release(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); streamDeleter.release(); *gifOut = gif.release(); } return true; }
/* * Initiates the gif decode */ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts, SkPMColor* inputColorPtr, int* inputColorCount, int* rowsDecoded) { Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts); if (kSuccess != result) { return result; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { return gif_error("Scaling not supported.\n", kInvalidScale); } // Initialize the swizzler if (fFrameIsSubset) { const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) { return gif_error("Could not initialize swizzler.\n", kUnimplemented); } // Fill the background SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()), opts.fZeroInitialized); // Modify the dst pointer const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType()); dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameRect.top() + dstBytesPerPixel * fFrameRect.left()); } else { if (kSuccess != this->initializeSwizzler(dstInfo, opts)) { return gif_error("Could not initialize swizzler.\n", kUnimplemented); } } // Iterate over rows of the input uint32_t height = fFrameRect.height(); for (uint32_t y = 0; y < height; y++) { if (!this->readRow()) { *rowsDecoded = y; return gif_error("Could not decode line.\n", kIncompleteInput); } void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y)); fSwizzler->swizzle(dstRow, fSrcBuffer.get()); } return kSuccess; }
SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, int* inputColorCount, const Options& opts) { // Check for valid input parameters if (!conversion_possible(dstInfo, this->getInfo())) { return gif_error("Cannot convert input type to output type.\n", kInvalidConversion); } // Initialize color table and copy to the client if necessary this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); return kSuccess; }
SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) { Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options()); if (kSuccess != result) { return result; } // Initialize the swizzler if (fFrameIsSubset) { const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) { return gif_error("Could not initialize swizzler.\n", kUnimplemented); } } else { if (kSuccess != this->initializeSwizzler(dstInfo, opts)) { return gif_error("Could not initialize swizzler.\n", kUnimplemented); } } return kSuccess; }
SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) { // Use this as a container to hold information about any gif extension // blocks. This generally stores transparency and animation instructions. SavedImage saveExt; SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); saveExt.ExtensionBlocks = nullptr; saveExt.ExtensionBlockCount = 0; GifByteType* extData; int32_t extFunction; // We will loop over components of gif images until we find an image. Once // we find an image, we will decode and return it. While many gif files // contain more than one image, we will simply decode the first image. GifRecordType recordType; do { // Get the current record type if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) { return gif_error("DGifGetRecordType failed.\n", kInvalidInput); } switch (recordType) { case IMAGE_DESC_RECORD_TYPE: { *transIndex = find_trans_index(saveExt); // FIXME: Gif files may have multiple images stored in a single // file. This is most commonly used to enable // animations. Since we are leaving animated gifs as a // TODO, we will return kSuccess after decoding the // first image in the file. This is the same behavior // as SkImageDecoder_libgif. // // Most times this works pretty well, but sometimes it // doesn't. For example, I have an animated test image // where the first image in the file is 1x1, but the // subsequent images are meaningful. This currently // displays the 1x1 image, which is not ideal. Right // now I am leaving this as an issue that will be // addressed when we implement animated gifs. // // It is also possible (not explicitly disallowed in the // specification) that gif files provide multiple // images in a single file that are all meant to be // displayed in the same frame together. I will // currently leave this unimplemented until I find a // test case that expects this behavior. return kSuccess; } // Extensions are used to specify special properties of the image // such as transparency or animation. case EXTENSION_RECORD_TYPE: // Read extension data if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) { return gif_error("Could not get extension.\n", kIncompleteInput); } // Create an extension block with our data while (nullptr != extData) { // Add a single block #if GIFLIB_MAJOR < 5 if (AddExtensionBlock(&saveExt, extData[0], &extData[1]) == GIF_ERROR) { #else if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount, &saveExt.ExtensionBlocks, extFunction, extData[0], &extData[1])) { #endif return gif_error("Could not add extension block.\n", kIncompleteInput); } // Move to the next block if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) { return gif_error("Could not get next extension.\n", kIncompleteInput); } } break; // Signals the end of the gif file case TERMINATE_RECORD_TYPE: break; default: // DGifGetRecordType returns an error if the record type does // not match one of the above cases. This should not be // reached. SkASSERT(false); break; } } while (TERMINATE_RECORD_TYPE != recordType); return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput); } bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) { // Get the encoded dimension values SavedImage* image = &gif->SavedImages[gif->ImageCount - 1]; const GifImageDesc& desc = image->ImageDesc; int frameLeft = desc.Left; int frameTop = desc.Top; int frameWidth = desc.Width; int frameHeight = desc.Height; int width = gif->SWidth; int height = gif->SHeight; // Ensure that the decode dimensions are large enough to contain the frame width = SkTMax(width, frameWidth + frameLeft); height = SkTMax(height, frameHeight + frameTop); // All of these dimensions should be positive, as they are encoded as unsigned 16-bit integers. // It is unclear why giflib casts them to ints. We will go ahead and check that they are // in fact positive. if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || width <= 0 || height <= 0) { return false; } frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight); size->set(width, height); return true; }
/* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. * * @param codecOut * If it returned true, and codecOut was not nullptr, * codecOut will be set to a new SkGifCodec. * * @param gifOut * If it returned true, and codecOut was nullptr, * gifOut must be non-nullptr and gifOut will be set to a new * GifFileType pointer. * * @param stream * Deleted on failure. * codecOut will take ownership of it in the case where we created a codec. * Ownership is unchanged when we returned a gifOut. * */ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { SkAutoTDelete<SkStream> streamDeleter(stream); // Read gif header, logical screen descriptor, and global color table SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); if (nullptr == gif) { gif_error("DGifOpen failed.\n"); return false; } // Read through gif extensions to get to the image data. Set the // transparent index based on the extension data. uint32_t transIndex; SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); if (kSuccess != result){ return false; } // Read the image descriptor if (GIF_ERROR == DGifGetImageDesc(gif)) { return false; } // If reading the image descriptor is successful, the image count will be // incremented. SkASSERT(gif->ImageCount >= 1); if (nullptr != codecOut) { SkISize size; SkIRect frameRect; if (!GetDimensions(gif, &size, &frameRect)) { gif_error("Invalid gif size.\n"); return false; } bool frameIsSubset = (size != frameRect.size()); // Determine the recommended alpha type. The transIndex might be valid if it less // than 256. We are not certain that the index is valid until we process the color // table, since some gifs have color tables with less than 256 colors. If // there might be a valid transparent index, we must indicate that the image has // alpha. // In the case where we must support alpha, we have the option to set the // suggested alpha type to kPremul or kUnpremul. Both are valid since the alpha // component will always be 0xFF or the entire 32-bit pixel will be set to zero. // We prefer kPremul because we support kPremul, and it is more efficient to use // kPremul directly even when kUnpremul is supported. SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType; // Return the codec // kIndex is the most natural color type for gifs, so we set this as // the default. SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType, alphaType); *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); streamDeleter.detach(); *gifOut = gif.detach(); } return true; }
SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) { // Use this as a container to hold information about any gif extension // blocks. This generally stores transparency and animation instructions. SavedImage saveExt; SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt); saveExt.ExtensionBlocks = nullptr; saveExt.ExtensionBlockCount = 0; GifByteType* extData; int32_t extFunction; // We will loop over components of gif images until we find an image. Once // we find an image, we will decode and return it. While many gif files // contain more than one image, we will simply decode the first image. GifRecordType recordType; do { // Get the current record type if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) { return gif_error("DGifGetRecordType failed.\n", kInvalidInput); } switch (recordType) { case IMAGE_DESC_RECORD_TYPE: { *transIndex = find_trans_index(saveExt); // FIXME: Gif files may have multiple images stored in a single // file. This is most commonly used to enable // animations. Since we are leaving animated gifs as a // TODO, we will return kSuccess after decoding the // first image in the file. This is the same behavior // as SkImageDecoder_libgif. // // Most times this works pretty well, but sometimes it // doesn't. For example, I have an animated test image // where the first image in the file is 1x1, but the // subsequent images are meaningful. This currently // displays the 1x1 image, which is not ideal. Right // now I am leaving this as an issue that will be // addressed when we implement animated gifs. // // It is also possible (not explicitly disallowed in the // specification) that gif files provide multiple // images in a single file that are all meant to be // displayed in the same frame together. I will // currently leave this unimplemented until I find a // test case that expects this behavior. return kSuccess; } // Extensions are used to specify special properties of the image // such as transparency or animation. case EXTENSION_RECORD_TYPE: // Read extension data if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) { return gif_error("Could not get extension.\n", kIncompleteInput); } // Create an extension block with our data while (nullptr != extData) { // Add a single block if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount, &saveExt.ExtensionBlocks, extFunction, extData[0], &extData[1])) { return gif_error("Could not add extension block.\n", kIncompleteInput); } // Move to the next block if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) { return gif_error("Could not get next extension.\n", kIncompleteInput); } } break; // Signals the end of the gif file case TERMINATE_RECORD_TYPE: break; default: // DGifGetRecordType returns an error if the record type does // not match one of the above cases. This should not be // reached. SkASSERT(false); break; } } while (TERMINATE_RECORD_TYPE != recordType); return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput); }
static void read_metadata (TrackerSparqlBuilder *preupdate, TrackerSparqlBuilder *metadata, GString *where, GifFileType *gifFile, const gchar *uri, const gchar *graph) { GifRecordType RecordType; int frameheight; int framewidth; unsigned char *framedata = NULL; GPtrArray *keywords; guint i; int status; MergeData md = { 0 }; GifData gd = { 0 }; TrackerXmpData *xd = NULL; do { GifByteType *ExtData; int ExtCode; ExtBlock extBlock; if (DGifGetRecordType(gifFile, &RecordType) == GIF_ERROR) { #if GIFLIB_MAJOR < 5 PrintGifError (); #else /* GIFLIB_MAJOR < 5 */ gif_error ("Could not read next GIF record type", gifFile->Error); #endif /* GIFLIB_MAJOR < 5 */ return; } switch (RecordType) { case IMAGE_DESC_RECORD_TYPE: if (DGifGetImageDesc(gifFile) == GIF_ERROR) { #if GIFLIB_MAJOR < 5 PrintGifError(); #else /* GIFLIB_MAJOR < 5 */ gif_error ("Could not get GIF record information", gifFile->Error); #endif /* GIFLIB_MAJOR < 5 */ return; } framewidth = gifFile->Image.Width; frameheight = gifFile->Image.Height; framedata = g_malloc (framewidth*frameheight); if (DGifGetLine(gifFile, framedata, framewidth*frameheight)==GIF_ERROR) { #if GIFLIB_MAJOR < 5 PrintGifError(); #else /* GIFLIB_MAJOR < 5 */ gif_error ("Could not load a block of GIF pixes", gifFile->Error); #endif /* GIFLIB_MAJOR < 5 */ return; } gd.width = g_strdup_printf ("%d", framewidth); gd.height = g_strdup_printf ("%d", frameheight); g_free (framedata); break; case EXTENSION_RECORD_TYPE: extBlock.bytes = NULL; extBlock.byteCount = 0; if ((status = DGifGetExtension (gifFile, &ExtCode, &ExtData)) != GIF_OK) { g_warning ("Problem getting the extension"); return; } #if defined(HAVE_EXEMPI) if (ExtData && *ExtData && strncmp (&ExtData[1],"XMP Data",8) == 0) { while (ExtData != NULL && status == GIF_OK ) { if ((status = DGifGetExtensionNext (gifFile, &ExtData)) == GIF_OK) { if (ExtData != NULL) { if (ext_block_append (&extBlock, ExtData[0]+1, (char *) &(ExtData[0])) != GIF_OK) { g_warning ("Problem with extension data"); return; } } } } xd = tracker_xmp_new (extBlock.bytes, extBlock.byteCount-XMP_MAGIC_TRAILER_LENGTH, uri); g_free (extBlock.bytes); } else #endif /* See Section 24. Comment Extension. in the GIF format definition */ if (ExtCode == EXTENSION_RECORD_COMMENT_BLOCK_CODE && ExtData && *ExtData) { guint block_count = 0; /* Merge all blocks */ do { block_count++; g_debug ("Comment Extension block found (#%u, %u bytes)", block_count, ExtData[0]); if (ext_block_append (&extBlock, ExtData[0], (char *) &(ExtData[1])) != GIF_OK) { g_warning ("Problem with Comment extension data"); return; } } while (((status = DGifGetExtensionNext(gifFile, &ExtData)) == GIF_OK) && ExtData != NULL); /* Add last NUL byte */ g_debug ("Comment Extension blocks found (%u) with %u bytes", block_count, extBlock.byteCount); extBlock.bytes = g_realloc (extBlock.bytes, extBlock.byteCount + 1); extBlock.bytes[extBlock.byteCount] = '\0'; /* Set commentt */ gd.comment = extBlock.bytes; } else { do { status = DGifGetExtensionNext(gifFile, &ExtData); } while ( status == GIF_OK && ExtData != NULL); } break; case TERMINATE_RECORD_TYPE: break; default: break; } } while (RecordType != TERMINATE_RECORD_TYPE); if (!xd) { xd = g_new0 (TrackerXmpData, 1); } md.title = tracker_coalesce_strip (3, xd->title, xd->title2, xd->pdf_title); md.date = tracker_coalesce_strip (2, xd->date, xd->time_original); md.artist = tracker_coalesce_strip (2, xd->artist, xd->contributor); if (xd->license) { tracker_sparql_builder_predicate (metadata, "nie:license"); tracker_sparql_builder_object_unvalidated (metadata, xd->license); } if (xd->creator) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", xd->creator); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, xd->creator); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:creator"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } tracker_guarantee_date_from_file_mtime (metadata, "nie:contentCreated", md.date, uri); if (xd->description) { tracker_sparql_builder_predicate (metadata, "nie:description"); tracker_sparql_builder_object_unvalidated (metadata, xd->description); } if (xd->copyright) { tracker_sparql_builder_predicate (metadata, "nie:copyright"); tracker_sparql_builder_object_unvalidated (metadata, xd->copyright); } if (xd->make || xd->model) { gchar *equip_uri; equip_uri = tracker_sparql_escape_uri_printf ("urn:equipment:%s:%s:", xd->make ? xd->make : "", xd->model ? xd->model : ""); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, equip_uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nfo:Equipment"); if (xd->make) { tracker_sparql_builder_predicate (preupdate, "nfo:manufacturer"); tracker_sparql_builder_object_unvalidated (preupdate, xd->make); } if (xd->model) { tracker_sparql_builder_predicate (preupdate, "nfo:model"); tracker_sparql_builder_object_unvalidated (preupdate, xd->model); } if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nfo:equipment"); tracker_sparql_builder_object_iri (metadata, equip_uri); g_free (equip_uri); } tracker_guarantee_title_from_file (metadata, "nie:title", md.title, uri, NULL); if (md.artist) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", md.artist); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, md.artist); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:contributor"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (xd->orientation) { tracker_sparql_builder_predicate (metadata, "nfo:orientation"); tracker_sparql_builder_object_unvalidated (metadata, xd->orientation); } if (xd->exposure_time) { tracker_sparql_builder_predicate (metadata, "nmm:exposureTime"); tracker_sparql_builder_object_unvalidated (metadata, xd->exposure_time); } if (xd->iso_speed_ratings) { tracker_sparql_builder_predicate (metadata, "nmm:isoSpeed"); tracker_sparql_builder_object_unvalidated (metadata, xd->iso_speed_ratings); } if (xd->white_balance) { tracker_sparql_builder_predicate (metadata, "nmm:whiteBalance"); tracker_sparql_builder_object_unvalidated (metadata, xd->white_balance); } if (xd->fnumber) { tracker_sparql_builder_predicate (metadata, "nmm:fnumber"); tracker_sparql_builder_object_unvalidated (metadata, xd->fnumber); } if (xd->flash) { tracker_sparql_builder_predicate (metadata, "nmm:flash"); tracker_sparql_builder_object_unvalidated (metadata, xd->flash); } if (xd->focal_length) { tracker_sparql_builder_predicate (metadata, "nmm:focalLength"); tracker_sparql_builder_object_unvalidated (metadata, xd->focal_length); } if (xd->metering_mode) { tracker_sparql_builder_predicate (metadata, "nmm:meteringMode"); tracker_sparql_builder_object_unvalidated (metadata, xd->metering_mode); } keywords = g_ptr_array_new (); if (xd->keywords) { tracker_keywords_parse (keywords, xd->keywords); } if (xd->pdf_keywords) { tracker_keywords_parse (keywords, xd->pdf_keywords); } if (xd->rating) { tracker_sparql_builder_predicate (metadata, "nao:numericRating"); tracker_sparql_builder_object_unvalidated (metadata, xd->rating); } if (xd->subject) { tracker_keywords_parse (keywords, xd->subject); } if (xd->regions) { tracker_xmp_apply_regions (preupdate, metadata, graph, xd); } for (i = 0; i < keywords->len; i++) { gchar *p, *escaped, *var; p = g_ptr_array_index (keywords, i); escaped = tracker_sparql_escape_string (p); var = g_strdup_printf ("tag%d", i + 1); /* ensure tag with specified label exists */ tracker_sparql_builder_append (preupdate, "INSERT { "); if (graph) { tracker_sparql_builder_append (preupdate, "GRAPH <"); tracker_sparql_builder_append (preupdate, graph); tracker_sparql_builder_append (preupdate, "> { "); } tracker_sparql_builder_append (preupdate, "_:tag a nao:Tag ; nao:prefLabel \""); tracker_sparql_builder_append (preupdate, escaped); tracker_sparql_builder_append (preupdate, "\""); if (graph) { tracker_sparql_builder_append (preupdate, " } "); } tracker_sparql_builder_append (preupdate, " }\n"); tracker_sparql_builder_append (preupdate, "WHERE { FILTER (NOT EXISTS { " "?tag a nao:Tag ; nao:prefLabel \""); tracker_sparql_builder_append (preupdate, escaped); tracker_sparql_builder_append (preupdate, "\" }) }\n"); /* associate file with tag */ tracker_sparql_builder_predicate (metadata, "nao:hasTag"); tracker_sparql_builder_object_variable (metadata, var); g_string_append_printf (where, "?%s a nao:Tag ; nao:prefLabel \"%s\" .\n", var, escaped); g_free (var); g_free (escaped); g_free (p); } g_ptr_array_free (keywords, TRUE); if (xd->publisher) { gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", xd->publisher); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, uri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:Contact"); tracker_sparql_builder_predicate (preupdate, "nco:fullname"); tracker_sparql_builder_object_unvalidated (preupdate, xd->publisher); if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); tracker_sparql_builder_predicate (metadata, "nco:creator"); tracker_sparql_builder_object_iri (metadata, uri); g_free (uri); } if (xd->type) { tracker_sparql_builder_predicate (metadata, "dc:type"); tracker_sparql_builder_object_unvalidated (metadata, xd->type); } if (xd->format) { tracker_sparql_builder_predicate (metadata, "dc:format"); tracker_sparql_builder_object_unvalidated (metadata, xd->format); } if (xd->identifier) { tracker_sparql_builder_predicate (metadata, "dc:identifier"); tracker_sparql_builder_object_unvalidated (metadata, xd->identifier); } if (xd->source) { tracker_sparql_builder_predicate (metadata, "dc:source"); tracker_sparql_builder_object_unvalidated (metadata, xd->source); } if (xd->language) { tracker_sparql_builder_predicate (metadata, "dc:language"); tracker_sparql_builder_object_unvalidated (metadata, xd->language); } if (xd->relation) { tracker_sparql_builder_predicate (metadata, "dc:relation"); tracker_sparql_builder_object_unvalidated (metadata, xd->relation); } if (xd->coverage) { tracker_sparql_builder_predicate (metadata, "dc:coverage"); tracker_sparql_builder_object_unvalidated (metadata, xd->coverage); } if (xd->address || xd->state || xd->country || xd->city || xd->gps_altitude || xd->gps_latitude || xd-> gps_longitude) { tracker_sparql_builder_predicate (metadata, "slo:location"); tracker_sparql_builder_object_blank_open (metadata); /* GeoLocation */ tracker_sparql_builder_predicate (metadata, "a"); tracker_sparql_builder_object (metadata, "slo:GeoLocation"); if (xd->address || xd->state || xd->country || xd->city) { gchar *addruri; addruri = tracker_sparql_get_uuid_urn (); tracker_sparql_builder_predicate (metadata, "slo:postalAddress"); tracker_sparql_builder_object_iri (metadata, addruri); tracker_sparql_builder_insert_open (preupdate, NULL); if (graph) { tracker_sparql_builder_graph_open (preupdate, graph); } tracker_sparql_builder_subject_iri (preupdate, addruri); g_free (addruri); tracker_sparql_builder_predicate (preupdate, "a"); tracker_sparql_builder_object (preupdate, "nco:PostalAddress"); if (xd->address) { tracker_sparql_builder_predicate (preupdate, "nco:streetAddress"); tracker_sparql_builder_object_unvalidated (preupdate, xd->address); } if (xd->state) { tracker_sparql_builder_predicate (preupdate, "nco:region"); tracker_sparql_builder_object_unvalidated (preupdate, xd->state); } if (xd->city) { tracker_sparql_builder_predicate (preupdate, "nco:locality"); tracker_sparql_builder_object_unvalidated (preupdate, xd->city); } if (xd->country) { tracker_sparql_builder_predicate (preupdate, "nco:country"); tracker_sparql_builder_object_unvalidated (preupdate, xd->country); } if (graph) { tracker_sparql_builder_graph_close (preupdate); } tracker_sparql_builder_insert_close (preupdate); } if (xd->gps_altitude) { tracker_sparql_builder_predicate (metadata, "slo:altitude"); tracker_sparql_builder_object_unvalidated (metadata, xd->gps_altitude); } if (xd->gps_latitude) { tracker_sparql_builder_predicate (metadata, "slo:latitude"); tracker_sparql_builder_object_unvalidated (metadata, xd->gps_latitude); } if (xd->gps_longitude) { tracker_sparql_builder_predicate (metadata, "slo:longitude"); tracker_sparql_builder_object_unvalidated (metadata, xd->gps_longitude); } tracker_sparql_builder_object_blank_close (metadata); /* GeoLocation */ } if (xd->gps_direction) { tracker_sparql_builder_predicate (metadata, "nfo:heading"); tracker_sparql_builder_object_unvalidated (metadata, xd->gps_direction); } if (gd.width) { tracker_sparql_builder_predicate (metadata, "nfo:width"); tracker_sparql_builder_object_unvalidated (metadata, gd.width); g_free (gd.width); } if (gd.height) { tracker_sparql_builder_predicate (metadata, "nfo:height"); tracker_sparql_builder_object_unvalidated (metadata, gd.height); g_free (gd.height); } if (gd.comment) { tracker_sparql_builder_predicate (metadata, "nie:comment"); tracker_sparql_builder_object_unvalidated (metadata, gd.comment); g_free (gd.comment); } tracker_xmp_free (xd); }
G_MODULE_EXPORT gboolean tracker_extract_get_metadata (TrackerExtractInfo *info) { TrackerSparqlBuilder *preupdate, *metadata; goffset size; GifFileType *gifFile = NULL; GString *where; const gchar *graph; gchar *filename, *uri; GFile *file; int fd; #if GIFLIB_MAJOR >= 5 int err; #endif preupdate = tracker_extract_info_get_preupdate_builder (info); metadata = tracker_extract_info_get_metadata_builder (info); graph = tracker_extract_info_get_graph (info); file = tracker_extract_info_get_file (info); filename = g_file_get_path (file); size = tracker_file_get_size (filename); if (size < 64) { g_free (filename); return FALSE; } fd = tracker_file_open_fd (filename); if (fd == -1) { g_warning ("Could not open GIF file '%s': %s\n", filename, g_strerror (errno)); g_free (filename); return FALSE; } #if GIFLIB_MAJOR < 5 if ((gifFile = DGifOpenFileHandle (fd)) == NULL) { PrintGifError (); #else /* GIFLIB_MAJOR < 5 */ if ((gifFile = DGifOpenFileHandle (fd, &err)) == NULL) { gif_error ("Could not open GIF file with handle", err); #endif /* GIFLIB_MAJOR < 5 */ close (fd); return FALSE; } g_free (filename); tracker_sparql_builder_predicate (metadata, "a"); tracker_sparql_builder_object (metadata, "nfo:Image"); tracker_sparql_builder_object (metadata, "nmm:Photo"); where = g_string_new (""); uri = g_file_get_uri (file); read_metadata (preupdate, metadata, where, gifFile, uri, graph); tracker_extract_info_set_where_clause (info, where->str); g_string_free (where, TRUE); g_free (uri); if (DGifCloseFile (gifFile) != GIF_OK) { #if GIFLIB_MAJOR < 5 PrintGifError (); #else /* GIFLIB_MAJOR < 5 */ gif_error ("Could not close GIF file", gifFile->Error); #endif /* GIFLIB_MAJOR < 5 */ } return TRUE; }